Azure-上的-Linux-管理实用指南-全-

Azure 上的 Linux 管理实用指南(全)

原文:zh.annas-archive.org/md5/0EE39A6B040A18FF64595B6B3C82179F

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

关于

本节简要介绍了作者、本课程的覆盖范围、开始所需的技术技能,以及完成所有包含的活动和练习所需的硬件和软件要求。

关于《在 Azure 上进行实践 Linux 管理,第二版》

由于微软 Azure 在提供可扩展的云解决方案方面具有灵活性,它是管理所有工作负载的合适平台。你可以使用它来实现 Linux 虚拟机和容器,并使用开放 API 在开源语言中创建应用程序。

这本 Linux 管理书首先带领你了解 Linux 和 Azure 的基础知识,为后面更高级的 Linux 功能做准备。通过真实世界的例子,你将学习如何在 Azure 中部署虚拟机(VMs),扩展其功能,并有效地管理它们。你将管理容器并使用它们可靠地运行应用程序,在最后一章中,你将探索使用各种开源工具进行故障排除的技术。

通过本书,你将熟练掌握在 Azure 上管理 Linux 和利用部署所需的工具。

关于作者

Kamesh Ganesan 是一位云倡导者,拥有近 23 年的 IT 经验,涵盖了 Azure、AWS、GCP 和阿里云等主要云技术。他拥有超过 45 个 IT 认证,包括 5 个 AWS、3 个 Azure 和 3 个 GCP 认证。他担任过多个角色,包括认证的多云架构师、云原生应用架构师、首席数据库管理员和程序分析员。他设计、构建、自动化并交付了高质量、关键性和创新性的技术解决方案,帮助企业、商业和政府客户取得了巨大成功,并显著提高了他们的业务价值,采用了多云策略。

Rithin Skaria 是一位开源倡导者,在 Azure、AWS 和 OpenStack 中管理开源工作负载方面拥有超过 7 年的经验。他目前在微软工作,并参与了微软内部进行的几项开源社区活动。他是认证的微软培训师、Linux 基金会工程师和管理员、Kubernetes 应用程序开发人员和管理员,也是认证的 OpenStack 管理员。在 Azure 方面,他拥有 4 个认证,包括解决方案架构、Azure 管理、DevOps 和安全,他还在 Office 365 管理方面也有认证。他在多个开源部署以及这些工作负载迁移到云端的管理和迁移中发挥了重要作用。

Frederik Vos 居住在荷兰阿姆斯特丹附近的普尔梅伦德市,是一位高级虚拟化技术培训师,专注于 Citrix XenServer 和 VMware vSphere 等虚拟化技术。他专长于数据中心基础设施(虚拟化、网络和存储)和云计算(CloudStack、CloudPlatform、OpenStack 和 Azure)。他还是一位 Linux 培训师和倡导者。他具有教师的知识和系统管理员的实际经验。在过去的 3 年中,他一直在 ITGilde 合作社内担任自由培训师和顾问,为 Linux 基金会提供了许多 Linux 培训课程,比如针对 Azure 的 Linux 培训。

学习目标

通过本课程,你将能够:

  • 掌握虚拟化和云计算的基础知识

  • 了解文件层次结构并挂载新文件系统

  • 在 Azure Kubernetes 服务中维护应用程序的生命周期

  • 使用 Azure CLI 和 PowerShell 管理资源

  • 管理用户、组和文件系统权限

  • 使用 Azure 资源管理器重新部署虚拟机

  • 实施配置管理以正确配置 VM

  • 使用 Docker 构建容器

观众

如果您是 Linux 管理员或微软专业人士,希望在 Azure 中部署和管理工作负载,那么这本书适合您。虽然不是必需的,但了解 Linux 和 Azure 将有助于理解核心概念。

方法

本书提供了实践和理论知识的结合。它涵盖了引人入胜的现实场景,展示了 Linux 管理员如何在 Azure 平台上工作。每一章都旨在促进每项新技能的实际应用。

硬件要求

为了获得最佳的学生体验,我们建议以下硬件配置:

  • 处理器:Intel Core i5 或同等级

  • 内存:4 GB RAM(建议 8 GB)

  • 存储:35 GB 可用空间

软件要求

我们还建议您提前准备以下内容:

  • 安装有 Linux、Windows 10 或 macOS 操作系统的计算机

  • 互联网连接,以便连接到 Azure

约定

文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄显示如下:

“以下代码片段创建一个名为MyResource1的资源组,并指定 SKU 为Standard_LRS,该 SKU 代表了此上下文中的冗余选项。”

以下是一个代码示例块:

New-AzStorageAccount -Location westus '
  -ResourceGroupName MyResource1'
  -Name "<NAME>" -SkuName Standard_LRS

在许多情况下,我们使用了尖括号,<>。您需要用实际参数替换它,而不要在命令中使用这些括号。

下载资源

本书的代码包也托管在 GitHub 上,网址为github.com/PacktPublishing/Hands-On-Linux-Administration-on-Azure---Second-Edition。您可以在相关实例中找到本书使用的 YAML 和其他文件。

我们还有其他代码包,来自我们丰富的图书和视频目录,可在github.com/PacktPublishing/上找到。去看看吧!

第一章:探索微软 Azure 云

人们经常因为围绕云计算这个术语的模糊性而感到困惑。在这里,我们不是指诸如 OneDrive、Dropbox 等云存储解决方案。相反,我们指的是实际由组织、公司甚至个人使用的计算解决方案。

微软 Azure(以前称为Windows Azure)是微软的公共云计算平台。它提供了广泛的云服务,包括计算、分析、存储、网络等。如果你浏览 Azure 提供的服务列表,你会发现你几乎可以处理任何东西,从虚拟机到人工智能和机器学习。

从虚拟化的简要历史开始,我们将解释如何将物理硬件转化为虚拟化硬件,使得在许多方面超越了经典数据中心的边界成为可能。

之后,我们将解释云技术中使用的不同术语。

以下是我们将要涵盖的关键主题列表:

  • 计算、网络和存储的虚拟化

  • 云服务

  • 云类型

云计算基础

当你开始学习新的信息技术IT)学科时,通常会从研究基本概念(即理论)开始。然后你会熟悉架构,迟早你会开始动手实践,看看它在实践中是如何工作的。

然而,在云计算中,如果你不仅了解概念和架构,而且了解它的来源,那真的会很有帮助。我们不想给你上一堂历史课,但我们想向你展示过去的发明和想法仍然在现代云环境中使用。这将让你更好地理解云是什么,以及如何在你的组织中使用它。

以下是云计算的关键基础:

  • 虚拟化

  • 软件定义的数据中心SDDC

  • 面向服务的架构SOA

  • 云服务

  • 云类型

让我们逐个看看这些,并了解这些术语指的是什么。

虚拟化

在计算中,虚拟化指的是创建设备或资源的虚拟形式,比如服务器、存储设备、网络,甚至操作系统。虚拟化的概念出现在 IBM 在 20 世纪 60 年代末和 70 年代初开发其分时共享解决方案时。分时共享指的是在大量用户之间共享计算资源,提高用户的生产力,消除为每个用户购买计算机的需要。这是计算技术革命的开始,新计算机的购买成本大大降低,组织可以利用他们已经拥有的未充分利用的计算资源。

现在,这种虚拟化已经发展成基于容器的虚拟化。虚拟机有自己的操作系统,在物理服务器的顶部虚拟化;另一方面,一个机器上的容器(无论是物理的还是虚拟的)都共享相同的基础操作系统。我们将在第九章《Azure 中的容器虚拟化》中更多地讨论容器。

快进到 2001 年,另一种虚拟化类型被引入,称为硬件虚拟化,由 VMware 等公司推出。在他们的产品 VMware Workstation 中,他们在现有操作系统的顶部添加了一层,提供了一组标准硬件和内置软件,而不是物理元素来运行虚拟机。这个层被称为hypervisor。后来,他们建立了自己的操作系统,专门用于运行虚拟机:VMware ESXi(以前称为 ESX)。

2008 年,微软推出了 Hyper-V 产品,进入了硬件虚拟化市场,作为 Windows Server 2008 的可选组件。

硬件虚拟化就是将软件与硬件分离,打破硬件和软件之间的传统界限。一个 hypervisor 负责将虚拟资源映射到物理资源上。

这种类型的虚拟化是数据中心革命的推动者:

  • 由于标准硬件的设置,每个虚拟机都可以在安装了 hypervisor 的任何物理机器上运行。

  • 由于虚拟机彼此隔离,如果特定的虚拟机崩溃,它不会影响在同一 hypervisor 上运行的任何其他虚拟机。

  • 因为虚拟机只是一组文件,您可以有新的备份、移动虚拟机等可能性。

  • 新的选项变得可用,以改善工作负载的可用性,具有高可用性HA)和即使虚拟机仍在运行也可以迁移虚拟机的可能性。

  • 新的部署选项也变得可用,例如使用模板。

  • 还有关于中央管理、编排和自动化的新选项,因为一切都是软件定义的。

  • 隔离、保留和在需要时限制资源,在可能的情况下共享资源。

SDDC

当然,如果您可以将硬件转换为计算机软件,那么很快就会有人意识到您也可以对网络和存储进行相同的操作。

对于网络,一切都始于虚拟交换机的概念。与其他形式的硬件虚拟化一样,这只是在软件中构建网络交换机,而不是在硬件中构建。

互联网工程任务组IETF)开始着手一个名为转发和控制元素分离的项目,这是一个提议的标准接口,用于解耦控制平面和数据平面。2008 年,在斯坦福大学使用 OpenFlow 协议实现了这一目标的第一个真正的交换机实现。软件定义网络SDN)通常与 OpenFlow 协议相关联。

使用 SDN,您可以获得与计算机虚拟化相似的优势:

  • 中央管理、自动化和编排

  • 通过流量隔离和提供防火墙和安全策略,实现更加精细的安全性

  • 塑造和控制数据流量

  • HA 和可伸缩性的新选项

2009 年,软件定义存储SDS)的开发在一些公司开始,比如 Scality 和 Cleversafe。同样,这是关于抽象化:将服务(逻辑卷等)与物理存储元素解耦。

如果您深入研究 SDS 的概念,一些供应商为虚拟化的已有优势添加了新功能。您可以向虚拟机添加策略,定义您想要的选项:例如,数据复制或每秒输入/输出操作IOPS)的限制。这对于管理员来说是透明的;hypervisor 和存储层之间进行通信以提供功能。后来,这个概念也被一些 SDN 供应商采纳。

您实际上可以看到,虚拟化慢慢地将不同数据中心层的管理转变为更加面向服务的方法。

如果您可以虚拟化物理数据中心的每个组件,那么您就拥有了一个 SDDC。网络、存储和计算功能的虚拟化使得可以超越单一硬件的限制。通过将软件从硬件中抽象出来,SDDC 使得可以超越物理数据中心的边界。

在 SDDC 环境中,一切都是虚拟化的,并且通常完全由软件自动化。这完全改变了传统的数据中心概念。服务托管的位置或可用时间(24/7 或按需)并不重要。此外,还有可能监视服务,甚至添加自动报告和计费等选项,这些都会让最终用户感到满意。

SDDC 与云不同,甚至不同于在您的数据中心运行的私有云,但您可以争辩说,例如,Microsoft Azure 是 SDDC 的全面实现—Azure 从定义上来说是软件定义的。

SOA

在硬件虚拟化成为数据中心主流并且 SDN 和 SDS 的开发开始的同时,软件开发领域出现了一些新的东西:SOA,它提供了几个好处。以下是一些关键点:

  • 最小的服务可以相互通信,使用诸如简单对象访问协议SOAP)之类的协议。它们一起提供完整的基于 Web 的应用程序。

  • 服务的位置并不重要;服务必须意识到其他服务的存在,就是这样。

  • 服务是一种黑匣子;最终用户不需要知道盒子里面有什么。

  • 每个服务都可以被另一个服务替换。

对于最终用户来说,应用程序位于何处或由几个较小的服务组成并不重要。在某种程度上,这就像虚拟化:看起来是一个物理资源,例如存储LUN(逻辑单元编号),实际上可能包括多个位置的多个物理资源(存储设备)。正如前面提到的,如果一个服务意识到另一个服务的存在(它可能在另一个位置),它们将一起行动并交付应用程序。我们每天互动的许多网站都是基于 SOA 的。

虚拟化与 SOA 的结合为您提供了更多的可伸缩性、可靠性和可用性选项。

SOA 模型和 SDDC 之间存在许多相似之处,但也有区别:SOA 涉及不同服务之间的交互;SDDC 更多地涉及向最终用户提供服务。

SOA 的现代实现是微服务,由 Azure 等云环境提供,可以独立运行或在 Docker 等虚拟化容器中运行。

云服务

这就是那个神奇的词:云服务是由云解决方案或计算提供商(如 Microsoft Azure)提供给组织、公司或用户的任何服务。如果您想提供以下服务,则云服务是合适的:

  • 高度可用并始终按需提供。

  • 可以通过自助服务进行管理。

  • 具有可伸缩性,使用户可以进行升级(使硬件更强大)或扩展(添加额外节点)。

  • 具有弹性—根据业务需求动态扩展或收缩资源数量的能力。

  • 提供快速部署。

  • 可以完全自动化和编排。

除此之外,还有用于监视资源和新类型的计费选项的云服务:大多数情况下,您只需支付您使用的部分。

云技术是通过互联网提供服务,以便使组织能够访问诸如软件、存储、网络和其他类型的 IT 基础设施和组件等资源。

云可以为您提供许多服务类型。以下是最重要的几种:

  • 基础设施即服务IaaS):托管虚拟机的平台。在 Azure 中部署的虚拟机就是一个很好的例子。

  • 平台即服务PaaS):一个用于开发、构建和运行应用程序的平台,无需建立和运行自己的基础设施的复杂性。例如,有 Azure 应用服务,您可以将代码推送到 Azure,Azure 将为您托管基础设施。

  • 软件即服务SaaS):在云中运行的即插即用应用程序,例如 Office 365。

尽管上述是云服务的关键支柱,您可能也会听说FaaS函数即服务),CaaS容器即服务),SECaaS安全即服务),随着云中服务提供的数量日益增加,清单也在不断增加。Azure 中的函数应用将是 FaaS 的一个例子,Azure 容器服务将是 CaaS 的例子,Azure 活动目录将是 SECaaS 的例子。

云类型

云服务可以根据其位置或托管服务的平台进行分类。正如前一节中提到的,基于平台,我们可以将云服务分类为 IaaS、PaaS、SaaS 等;然而,基于位置,我们可以将云分类为:

  • 公共云:所有服务都由服务提供商托管。微软的 Azure 就是这种类型的实现。

  • 私有云:您自己的数据中心中的云。微软最近为此开发了 Azure 的特殊版本:Azure Stack。

  • 混合云:公共云和私有云的组合。一个例子是结合 Azure 和 Azure Stack 的强大功能,但您也可以考虑新的灾难恢复选项,或者在临时需要更多资源时将服务从您的数据中心移到云端,然后再移到回来。

  • 社区云:社区云是多个组织在同一共享平台上工作,前提是它们有类似的目标或目标。

选择这些云实现之一取决于几个因素;仅举几个例子:

  • 成本:将您的服务托管在云中可能比在本地托管它们更昂贵,这取决于资源使用情况。另一方面,它可能更便宜;例如,您不需要实施复杂和昂贵的可用性选项。

  • 法律限制:一些组织可能无法使用公共云。例如,美国政府有自己的名为 Azure Government 的 Azure 产品。同样,德国和中国也有他们自己的 Azure 产品。

  • 互联网连接:仍然有一些国家的必要带宽甚至连接的稳定性是一个问题。

  • 复杂性:特别是混合云环境可能难以管理;对应用程序和用户管理的支持可能具有挑战性。

了解微软 Azure 云

现在您已经更多地了解了虚拟化和云计算,是时候向您介绍云的微软实现:Azure。

重新开始,从一些历史开始,在这一部分,您将了解到 Azure 背后的技术,以及 Azure 可以成为您的组织的一个非常好的解决方案。

微软 Azure 云的简要历史

在 2002 年,微软启动了一个名为 Whitehorse 的项目,以简化在 SOA 模型中开发、部署和实施应用程序。在这个项目中,重点是提供小型、预构建的 Web 应用程序,并能够将它们转换为服务。这个项目在 2006 年左右悄然消失。

在那个项目中学到的许多经验教训以及亚马逊网络服务AWS)的出现,促使微软在 2006 年启动了一个名为RedDog的项目。

过了一段时间,微软将另外三个开发团队加入了这个项目:

  • .NET 服务:为使用 SOA 模型的开发人员提供的服务。.NET 服务提供了作为安全、基于标准的消息基础设施的服务总线。

  • Live 服务和 Live Mesh:一个 SaaS 项目,通过互联网使 PC 和其他设备能够相互通信。

  • SQL 服务:通过互联网提供微软 SQL 的 SaaS 项目。

2008 年,微软宣布启动 Azure,并在 2010 年公开发布时,Azure 已准备好提供 IaaS 和 PaaS 解决方案。RedDog 这个名字存活了一段时间:经典门户也被称为RedDog 前端RDFE)。经典门户基于服务管理模型。另一方面,Azure 门户基于Azure 资源管理器ARM)。这两个门户基于两种不同的 API。

如今,Azure 是微软三个云服务之一(其他两个是 Office 365 和 Xbox),用于提供不同类型的服务,如虚拟机、Web 和移动应用程序、Active Directory、数据库等。

在功能、客户和可用性方面,Azure 仍在不断增长。Azure 在超过 54 个区域可用。这对于可伸缩性、性能和冗余性非常重要。

拥有这么多的区域也有助于遵守法律和安全/隐私政策。有关安全、隐私和合规性的信息和文件可通过微软的信任中心获取:www.microsoft.com/en-us/TrustCenter

Azure 架构

Microsoft Azure 运行在定制的、精简的、加固的 Hyper-V 版本上,也被称为Azure Hypervisor

在这个虚拟化程序之上,有一个云层。这个层或基础架构是托管在微软数据中心中的许多主机的集群,负责部署、管理和维护基础设施的健康。

这个云层由基础架构控制器管理,负责资源管理、可伸缩性、可靠性和可用性。

这一层还通过基于 REST、HTTP 和 XML 的 API 提供管理界面。与基础架构控制器互动的另一种方式是通过 Azure 门户和诸如 Azure CLI 之类的软件通过 Azure 资源管理器。

以下是 Azure 架构的图示表示:

Azure 架构

图 1.1:Azure 架构

这些用户界面服务(Azure 门户、PowerShell、Azure CLI 和 API)将通过资源提供程序与基础架构进行通信。例如,如果您想要创建、删除或更新计算资源,用户将与称为计算资源提供程序CRP)的Microsoft.Compute资源提供程序进行交互。同样,网络资源通过网络资源提供程序NRP)或Microsoft.Network资源提供程序进行通信,存储资源通过存储资源提供程序SRP)或Microsoft.Storage资源提供程序进行通信。

这些资源提供程序将创建所需的服务,比如虚拟机。

您的组织中的 Azure

Azure 可以提供 IaaS:轻松部署虚拟机,手动或自动化,并使用这些虚拟机开发、测试和托管您的应用程序。还有许多额外的服务可用,使您作为系统工程师的生活更加轻松,如备份和恢复选项、添加存储和可用性选项。对于 Web 应用程序,甚至可以在不创建虚拟机的情况下提供服务!

当然,Azure 也可以用于 PaaS 解决方案;与 IaaS 一样,PaaS 包括基础架构的所有组件,但还支持云应用程序的完整生命周期:构建、测试、部署、管理和更新。还有预定义的应用程序组件可用;您可以节省时间,将这些组件与您的代码一起转换为您想要提供的服务。容器也可以成为 PaaS 解决方案的一部分。Azure 容器服务简化了使用 Kubernetes 或其他编排器(如 Mesos)部署、管理和操作容器。

如果你是一家希望在 Azure 中托管 SaaS 解决方案的公司或组织,这是可能的,使用 AppSource。你甚至可以与其他微软产品(如 Office 365 和 Dynamics)进行集成。

2017 年,微软宣布推出 Azure Stack。现在你可以在自己的数据中心或选择的服务提供商的数据中心中运行 Azure,以提供 IaaS 和 PaaS。它为你提供了 Azure 的可伸缩性和可用性,而无需担心配置。只有在需要时才需要添加更多的物理资源。如果你愿意,你可以将其用于与公共 Azure 的混合解决方案,用于灾难恢复或云和本地部署中的一致工作负载。

Azure Stack 不是你可以用于混合环境的唯一选择。例如,你可以将本地 Active Directory 与 Azure Active Directory 连接,或者使用 Azure Active Directory 应用程序为本地和托管的 Web 应用程序提供单一登录(SSO)。

Azure 和开源

2009 年,甚至在 Azure 公开之前,微软就开始为开源框架(如 PHP)添加支持,2012 年,由于许多客户的要求,微软添加了对 Linux 虚拟机的支持。

当时,微软并不是开源社区的好朋友,可以说他们真的不喜欢 Linux 操作系统。这种情况在 2014 年左右发生了变化,当时萨蒂亚·纳德拉(Satya Nadella)接替史蒂夫·鲍尔默(Steve Ballmer)担任微软首席执行官。在那一年的十月,他甚至在旧金山的一次微软大会上宣布微软热爱 Linux!

从那时起,Azure 已经发展成一个非常开源友好的环境:

  • 它为许多开源解决方案提供了平台,如 Linux 实例、容器技术和应用/开发框架。

  • 它通过提供开放和兼容的 API,与开源解决方案进行集成。例如,Cosmos DB 服务提供了与 MongoDB 兼容的 API。

  • 文档、软件开发工具包(SDK)和示例都是开源的,可以在 GitHub 上找到:github.com/Azure

  • 微软正在与开源项目和供应商合作,并且也是许多开源项目的主要代码贡献者。

2016 年,微软以白金会员的身份加入了 Linux 基金会组织,以确认他们对开源开发兴趣和参与度稳步增加。

2017 年 10 月,微软表示 Azure 中超过 40%的虚拟机正在运行 Linux 操作系统,Azure 正在运行许多容器化的工作负载。从当前的统计数据来看,工作负载的数量已经超过 60%。此外,微服务都在使用开源编程语言和接口。

微软非常重视开源技术、开源 PowerShell 和许多其他产品。并非 Azure 中的每个微软产品都是开源的,但至少你可以在 Linux 上安装和运行 Microsoft SQL,或者获取 Microsoft SQL 的容器镜像。

总结

在本章中,我们讨论了虚拟化的历史和云的概念,并解释了云环境中使用的术语。

有些人认为微软进入云世界有点晚了,但实际上,他们从 2006 年开始研究和开发技术,其中许多部分在 Azure 中得以保留。一些项目因为太早而夭折,当时很多人对云持怀疑态度。

我们还介绍了 Azure 云的架构和 Azure 可以为您的组织提供的服务。

在本章的最后部分,我们看到 Azure 是一个非常开源友好的环境,微软付出了很多努力,使 Azure 成为一个开放、标准的云解决方案,具有互操作性。

在下一章中,我们将开始使用 Azure,并学习如何在 Azure 中部署和使用 Linux。

问题

  1. 您的物理数据中心中的哪些组件可以转化为软件?

  2. 容器虚拟化和硬件虚拟化有什么区别?

  3. 如果您想在云中托管应用程序,哪种服务类型是最佳解决方案?

  4. 假设您的某个应用程序需要严格的隐私政策。对于您的组织来说,使用云技术仍然是一个好主意吗?

  5. 为什么 Azure 有这么多可用的区域?

  6. Azure Active Directory 的目的是什么?

进一步阅读

如果您想了解更多关于 Hyper-V 以及如何将 Azure 与 Hyper-V 一起用于站点恢复和工作负载保护的信息,请查看 Packt Publishing 的《Windows Server 2016 Hyper-V Cookbook, Second Edition》。

有许多关于虚拟化、云计算及它们之间关系的技术文章。我们想提到的一个是《虚拟化与云计算关系的正式讨论》(ISBN 978-1-4244-9110-0)。

不要忘记访问本章中提到的微软网站和 GitHub 存储库!

第二章:开始使用 Azure 云

在第一章中,我们介绍了虚拟化和云计算的历史和理念。之后,你了解了微软 Azure 云。本章将帮助你迈出 Azure 世界的第一步,获取 Azure 访问权限,探索不同的 Linux 提供,并部署你的第一个 Linux 虚拟机。

部署后,你将需要使用安全外壳SSH)进行密码验证或使用 SSH 密钥对访问你的虚拟机。

要开始你的 Azure 云之旅,完成所有练习并检查结果非常重要。在本章中,我们将使用 PowerShell 以及 Azure CLI。随意选择你喜欢的方式进行跟随;然而,学习两者都不会有坏处。本章的关键目标是:

  • 设置你的 Azure 帐户。

  • 使用 Azure CLI 和 PowerShell 登录 Azure。

  • Azure 资源管理器ARM)交互以创建网络和存储资源。

  • 了解 Linux 发行版和微软认可的发行版。

  • 部署你的第一个 Linux 虚拟机。

注意

本章中的所有内容都在 macOS、Linux 子系统和最新版本的 CentOS 和 openSUSE LEAP 上进行了测试。

技术要求

如果你想尝试本章中的所有示例,至少需要一个浏览器。出于稳定性的考虑,使用最新版本的浏览器非常重要。微软在官方 Azure 文档中提供了支持的浏览器列表:

  • 微软 Edge(最新版本)

  • Internet Explorer 11

  • Safari(最新版本,仅限 Mac)

  • Chrome(最新版本)

  • Firefox(最新版本)

根据个人经验,我们建议使用 Google Chrome 或基于其引擎最新版本的浏览器,如 Vivaldi。

你可以在浏览器中完成所有练习,甚至包括命令行练习。实际上,使用本地安装的 Azure CLI 或 PowerShell 是个好主意;它更快,更容易复制和粘贴代码,你还可以保存历史和命令的输出。

获取 Azure 访问权限

开始使用 Azure,你需要的第一件事是一个帐户。前往azure.microsoft.com并获取一个免费帐户开始,或者使用已经在使用的公司帐户。另一个可能性是使用 Visual Studio 专业版或企业版订阅的 Azure,这将为你提供 Azure 的微软开发者网络MSDN)积分。如果你的组织已经与微软签订了企业协议,你可以使用你的企业订阅,或者你可以注册一个按使用量付费的订阅(如果你已经使用了免费试用)。

如果你使用的是免费帐户,你将获得一些信用额度来开始,一些流行的服务有限时间内免费,以及一些永远免费的服务,如容器服务。你可以在azure.microsoft.com/en-us/free找到最新的免费服务列表。在试用期间,除了需要额外许可的虚拟机外,你不会被收费,但你需要一张信用卡来验证身份。

使用 Azure 门户登录

将浏览器指向portal.azure.com并使用你的凭据登录。你现在可以开始使用 Azure,或者换句话说,开始使用你的订阅。在 Azure 中,订阅允许你使用你的帐户使用 Azure 门户/Azure CLI/PowerShell 创建和部署资源。它也用于会计和计费。

Azure 门户将带你到一个仪表板,你可以根据自己的监控需求进行修改。你现在可以:

  • 检查你的资源。

  • 创建新资源。

  • 访问 Marketplace,这是一个在线商店,你可以购买和部署专为 Azure 云构建的应用程序或服务。

  • 了解您的计费情况。

您可以使用网络界面,以图形方式执行所有操作,或者通过网络界面使用 Azure Cloud Shell,它提供了 Bash 或 PowerShell 界面。

获取对 Azure 的命令行访问

有几个很好的理由使用命令行。这就是为什么在本书中,我们将主要介绍 Azure 命令行访问的原因:

  • 它可以帮助您了解 Azure 的架构。在图形界面中,通常可以在一个配置窗口中执行许多操作,而不必了解不同字段和组件之间的关系。

  • 这是自动化和编排的第一步。

  • 网络界面仍在积极开发中;网络界面可以并且将会随着时间改变:

某些功能和选项目前尚不可用。

微软可能会在网络界面中重新定位功能和选项。

  • 另一方面,命令行界面在语法和输出方面非常稳定。

在本书中,我们将在 Bash shell 中使用 Azure CLI 和带有 PowerShell Az 模块的 PowerShell。两者都非常适合,与平台无关,并且除了一两个例外之外,在功能上没有区别。选择您喜欢的,因为您已经熟悉它,或者尝试两种界面,然后选择。

注意

请注意,从本书中复制和粘贴命令可能会由于空格和缩进而导致错误。为了获得更好的结果,请始终输入命令。此外,这将帮助您熟悉命令。

安装 Azure CLI

如果您在 Azure Cloud Shell 中使用 Bash 界面,那么可以使用完整的 Linux 环境来安装 Azure 命令行界面。它还提供了 Azure 特定的命令,比如 az 命令。

您也可以在 Windows、macOS 和 Linux 上安装此实用程序。还提供了 Docker 容器。您可以在 docs.microsoft.com/en-us/cli/azure 找到所有这些平台的详细安装说明。

让我们以 CentOS/Red Hat Enterprise Linux (RHEL) 7 为例来安装 Azure CLI:

  1. 导入 Microsoft 存储库的 GNU 隐私卫士 (GPG) 密钥:
sudo rpm --import \ https://packages.microsoft.com/keys/microsoft.asc
  1. 添加存储库:
sudo yum-config-manager --add-repo= \    
  https://packages.microsoft.com/yumrepos/azure-cli
  1. 安装软件:
sudo yum install azure-cli
  1. 要在基于 Ubuntu 或 Debian 的系统上安装 Azure CLI,请使用以下命令:
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash 

在 macOS 上,您必须首先安装 Homebrew,这是一个免费的开源软件包管理系统,简化了大多数开源软件的安装。

  1. 打开终端并执行以下操作:
ruby -e "$(curl -fsSL \
 https://raw.githubusercontent.com/Homebrew/install/master/install)"
  1. 更新 Homebrew 并安装 Azure CLI:
brew update && brew install azure-cli
  1. 安装 Azure CLI 后,您可以使用以下命令验证已安装的版本:
az -v

使用 Azure CLI 登录

Azure CLI 是用于访问或管理 Azure 资源的命令行工具,好处是它适用于 macOS、Linux 和 Windows 平台。在使用 CLI 之前,您必须登录:

az login

此命令将打开浏览器,并要求您使用 Microsoft 帐户登录。如果出现错误,指出 shell 无法打开交互式浏览器,请使用 az login –use-device-code。这将生成一个代码,您可以在 www.microsoft.com/devicelogin 中使用它完成身份验证。

如果成功,它将以 JSON 格式给出一些关于您的订阅的输出,比如您的用户名:

[
   {
     "cloudName": "AzureCloud",
         "id": "....",
         "isDefault": true,
         "name": "Pay-As-You-Go",
         "state": "Enabled",
         "tenantId": "....",
         "user": {
            "name": "....",
            "type": "user"
          }
    }
 ]

要再次获取此信息,请输入以下内容:

az account list 

您可以始终使用额外的参数将输出格式化为 JSON、JSONC、TABLE 或 TSV 格式。

JSON(或 JSONC,彩色变体)格式在编程和脚本语言中更容易解析:

在命令提示符中显示订阅详细信息的输出以 JSON 格式

图 2.1:以 JSONC 格式显示的订阅详细信息

制表符分隔值TSV)是一个很好的主意,如果输出是单个值,如果您想要使用文本过滤实用程序(如 AWK),或者如果您想要将输出导出到电子表格中:

输出显示命令提示符上的订阅详细信息,由制表符分隔

图 2.2:以制表符分隔的订阅详细信息

表格输出非常易于阅读,但比默认输出更受限制:

输出显示命令提示符上的订阅详细信息,以表格格式显示

图 2.3:表格格式的订阅详细信息

要以表格格式获取已登录帐户具有访问权限的订阅列表,请执行以下命令:

az account list -o table

为了更容易阅读 JSON 输出,您还可以查询特定字段:

az account list -o table --query '[].[user.name]'

如果您已经拥有大量资源或帐户,浏览整个列表将非常困难。幸运的是,有一种方法可以深入了解输出并仅获取所需的信息。使用--query参数链接命令将帮助您执行此操作,使用称为 JMESPATH(jmespath.org)的强大查询语言。再次查看az account list命令的 JSON 输出。此查询正在搜索user字段和name属性。

让我们回到登录过程。每次都这样做可能不是最用户友好的过程。更好的方法是创建服务主体,也称为应用程序注册,为特定应用程序提供凭据:

az ad sp create-for-rbac --name <APP_NAME>

您可以为应用程序提供名称,但是某些特殊字符是不允许的。原因是APP_NAME将创建一个 URL,因此在 URL 中禁止的所有字符都不能添加到APP_NAME中(例如@和%)。再次以 JSON 格式输出,将提供应用程序 ID(appID参数):

{
    "appID": "....",
    "displayName": "APP_NAME",
    "name": "http://APP_NAME",
    "password": "....",
    "tenant": "...."
 }

请在记事本上记下输出,因为我们将使用这些值进行身份验证。应用程序或服务主体代表 Azure 租户中的对象。租户是指一个组织,通常表示为<yourcompany/yourname>.onmicrosoft.com,它管理和拥有 Microsoft 云服务的实例。从 Azure 的角度来看,部署的所有服务都将与订阅相关联,并且订阅将映射到租户。一个租户可以拥有托管不同服务的多个订阅。从前面的输出中,我们将获得以下值:

  • appID:应用程序 ID 类似于应用程序的用户名。我们将在登录时使用此 ID 作为用户名。

  • displayName:在创建应用程序时为应用程序指定的友好名称。我们通过name参数设置名称。

  • name:基于我们给定的名称的 URL。

  • password:这是我们创建的服务主体的密码。在登录时,我们将在密码字段中使用此值。

  • tenant:租户 ID;我们在前一段中讨论了租户。

需要访问的应用程序必顶由安全主体表示。安全主体定义了租户中用户/应用程序的访问策略和权限。这使得在登录期间对用户/应用程序进行身份验证,并在资源访问期间进行基于角色的授权成为可能。总之,您可以使用appID进行登录。

列出分配给新创建的appID的角色:

az role assignment list --assignee <appID> --o table

默认情况下使用贡献者角色。此角色具有对 Azure 帐户的读写权限。

现在,测试一下并注销:

az logout

现在,再次使用appID登录。您可以使用之前复制的值来完成身份验证:

az login --service-principal --username <appID> --tenant <tenant id>

不幸的是,没有办法将用户名、appIDtenant id存储在配置文件中。可选地,您可以将--password添加到命令中:

az login --service-principal --username <appID> --tenant <tenant id> --password <app_password> 

除了使用az命令输入完整命令之外,还可以以交互式 shell 模式打开它:

az interactive

这个 shell 最大的特点之一是它将终端分成两个窗口。在上屏幕上,您可以输入命令;在下屏幕上,您在输入命令时会得到帮助。命令、参数和通常参数值也支持自动完成。

PowerShell

PowerShell 是由 Microsoft 开发的脚本语言,集成到.NET Framework 中。它是由 Jeffrey Snover、Bruce Payette 和 James Truher 于 2006 年设计的。PowerShell 不仅适用于 Windows,还适用于 Linux 和 macOS。您可以在 PowerShell 的 GitHub 存储库上找到使用这些操作系统的详细说明:github.com/PowerShell

例如,在 RHEL 或 CentOS 中安装它,请按照以下步骤操作:

  1. 如果您在安装 Azure CLI 时没有导入 Microsoft 存储库的 GPG 密钥,请执行此操作:
sudo rpm –import \  https://packages.microsoft.com/keys/microsoft.asc
  1. 添加存储库:
sudo yum-config-manager --add-repo= \https://packages.microsoft.com/rhel/7/prod/
  1. 安装软件:
sudo yum install -y powershell
  1. 使用pwsh -v显示已安装的版本。

  2. 输入 PowerShell:

pwsh

在 macOS 上,您需要 Homebrew 和 Homebrew Cask。Cask 扩展了 Homebrew 以安装更多和更大的应用程序:

  1. 安装 Homebrew Cask:
brew tap caskroom/cask
  1. 安装 PowerShell:
brew cask install powershell
  1. 使用pwsh -v显示已安装的版本。

  2. 进入 PowerShell:

pwsh

安装 PowerShell 后,您可以安装 Az 模块。根据您的互联网速度,下载模块可能需要一些时间。您将能够在 shell 中看到下载的进度:

Install-Module -Name Az -AllowClobber -Scope CurrentUser -Force

PowerShell 使用PowerShellGet cmdlet 从 PowerShell Gallery 下载模块及其依赖项,PowerShell Gallery 是一个托管许多模块的在线存储库。请注意,您需要在 Windows 和 Linux 中具有管理员权限才能执行此操作。PowerShell Gallery 未配置为受信任的存储库:

Untrusted repository
You are installing the modules from an untrusted repository. If you trust this 
repository, change its InstallationPolicy value by running the Set-PSRepository
 cmdlet. Are you sure you want to install the modules from 'PSGallery'?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help 
(default is "N"): A

在列表中用[A] Yes to All回答问题。

现在可能由于force参数,您安装了多个 Az 模块的版本。您可以使用以下命令验证多个版本的存在:

Get-InstalledModule -Name Az -AllVersions | '       select Name,Version 

默认情况下将使用最新版本,除非在导入模块时使用-RequiredVersion参数。

使用 PowerShell 登录

安装完成后,导入模块:

Import-Module -name Az

如果您不创建 PowerShell 脚本,而只在与 Azure 交互时在 PowerShell 环境中执行命令,您将需要再次执行此命令。但是,如果您愿意,您可以自动加载模块。

首先,通过执行以下命令找出您的 PowerShell 配置文件在文件系统上的位置:

$profile

在文本编辑器中打开或创建此文件,并添加以下行:

Import-Module -name Az

注意

在实际创建此文件之前,可能需要创建目录结构。

现在您可以执行所有可用的 Azure 命令。

使用以下 cmdlet 登录:

Connect-AzAccount

这将打开一个交互式浏览器窗口,您可以使用您的凭据进行身份验证。如果结果没有显示租户 ID,请执行此操作:

Get-AzContext -ListAvailable | select Tenant

现在,使用您找到的租户 ID 再次登录:

Connect-AzAccount -Tenant <tenantID>

如果您有多个订阅,您可以添加-Subscription参数和订阅 ID。如前所述,创建服务主体可能是一个好主意:

$newsp = New-AzADServicePrincipal ' -DisplayName "APP_NAME" -Role Contributor

如果您不提及DisplayName,这是服务主体的友好名称,Azure 将以格式 azure-powershell-MM-dd-yyyy-HH-mm-ss 生成一个名称。接下来,您需要检索新创建的服务主体的应用程序 ID:

$newsp.ApplicationId

密码可以存储到一个变量中,该变量将被加密,我们必须解密它:

$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($newsp.Secret)
$UnsecureSecret = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)

$UnsecureSecret变量包含服务主体的密码。

为了能够进行身份验证,我们需要服务主体的凭据:

$creds = Get-Credential 

提供ApplicationID和密码,它们分别存储在$newsp.ApplicationId$UnsecureSecret变量中。现在我们有了使用这些凭据连接到 Azure 所需的一切:

Connect-AzAccount -Credential $creds '
  -Tenant <tentant id> '
  -ServicePrincipal

现在,保存上下文:

Save-AzContext -Path $HOME/.Azure/AzureContext.json

必要时覆盖现有内容。退出 PowerShell 环境并执行 PowerShell。确保你已登录到 Azure 并使用以下命令验证上下文:

Get-AzContext

Azure 资源管理器

在开始部署你的第一个 Linux 虚拟机之前,了解Azure 资源管理器ARM)更加重要。

基本上,ARM 使你能够使用诸如存储和虚拟机之类的资源。为此,你必须创建一个或多个资源组,以便你可以执行生命周期操作,如在一个操作中部署、更新和删除资源组中的所有资源。

注意

资源组必须在一个区域中创建,也被称为位置。请注意,不同区域提供的服务可能会有所不同。要了解更多关于这些差异的信息,请访问azure.microsoft.com/en-us/global-infrastructure/services/

Azure 有超过 54 个区域。如果一个位置不可用,需要为你的账户加入白名单。为此,你可以联系微软支持。要获取你的账户可用位置和支持的资源提供程序列表,请在 PowerShell 中执行以下命令:

Get-AzLocation | Select-Object Location

你也可以在 Bash 中执行以下操作:

az account list-locations --query '[].name'

然后,在其中一个区域创建一个资源组:

New-AzResourceGroup -Location westus2 -Name 'MyResource1'

现在,验证结果:

Get-AzResourceGroup | Format-Table

这是前述命令的 Bash 版本:

az group create --location westus2 --name MyResource2

要验证Azure 资源管理器(ARM)的结果,执行以下命令:

az group list -o table

除了使用区域和资源组,你还必须了解存储冗余的概念。可用的复制选项如下:

  • Standard_LRS:本地冗余存储

Premium_LRS:与 LRS 相同,但也支持文件存储。

Standard_GRS:地理冗余存储

Standard_RAGRS:读取访问地理冗余存储

  • Standard_ZRS:区域冗余存储;ZRS 不支持 Blob 存储

注意

更多信息可在微软网站上找到:docs.microsoft.com/en-us/azure/storage/common/storage-redundancy

理解这个概念很重要,因为与你的资源组一起,一个存储账户在一个区域是必需的。存储账户在 Azure 中提供了一个唯一的命名空间来存储数据(如诊断)和使用 Azure Files 等服务的可能性。要为这些数据配置冗余,你必须指定在这种情况下代表冗余选项的 SKU:

New-AzStorageAccount -Location westus '
  -ResourceGroupName MyResource1'
  -Name "<NAME>" -SkuName Standard_LRS

或者你可以通过 Azure CLI 执行:

az storage account create --resource-group MyResource2 
  --sku Standard_LRS --name <NAME>

存储账户名称必须在 Azure 中是唯一的,长度在 3 到 24 个字符之间,并且只能使用数字和小写字母。

Linux 和 Azure

Linux 几乎无处不在,出现在许多不同的设备和环境中。有许多不同的风味,你可以选择使用什么。那么,你会选择什么?有很多问题,也有很多不同的答案。但有一件事是肯定的:在企业环境中,支持是很重要的。

Linux 发行版

如前所述,周围有许多不同的 Linux 发行版。但为什么有这么多选择呢?有很多原因:

  • Linux 发行版是一组软件。有些集合是为了特定的目标。这样一个发行版的一个很好的例子是 Kali Linux,它是一个先进的渗透测试 Linux 发行版。

  • Linux 是一个多用途操作系统。由于我们对 Linux 有很多定制选项,如果你不想要操作系统上的特定软件包或功能,你可以删除它并添加自己的。这是为什么有这么多发行版的主要原因之一。

  • 开源天生是达尔文主义的。有时,一个项目会被分叉,例如因为其他开发人员不喜欢项目的目标,或者认为他们可以做得更好,而项目的补丁没有被接受。只有最强大的项目才能生存下来。

  • 这是一个品味问题。不同的人有不同的品味和观点。有些人喜欢 Debian 的apt软件包管理器;其他人可能喜欢 SUSE 的 Zypper 工具。

  • 另一个重要的区别是,一些发行版是由 Red Hat、SUSE 和 Canonical 等供应商收集和支持的,而另一些如 Debian 则是由社区驱动的。

在生产环境中,支持是很重要的。在将他们的生产工作负载推送到一个发行版之前,组织将关注某些因素,如 SLA、停机时间和安全更新,可能会出现以下问题:

  • 谁负责更新,更新中包含什么样的信息?

  • 谁负责支持,如果出现问题我该找谁?

  • 如果软件许可存在法律问题,谁会为我提供建议?

微软认可的 Linux 发行版

在 Azure 市场上,有第三方提供的 Linux 映像,也称为微软合作伙伴提供的 Microsoft 认可的 Linux 发行版。

Microsoft 与这些合作伙伴和 Linux 社区一起合作,以确保这些 Linux 发行版在 Azure 上运行良好。

您可以将自己的映像,甚至自己的 Linux 发行版导入 Azure。微软直接为 Linux 内核做出贡献,为 Hyper-V 和 Azure 提供 Linux 集成服务,因此只要支持编译到内核中,您就可以在 Azure 上运行任何 Linux 发行版。此外,在 Azure 市场上的每个 Linux 映像中都安装了 Azure Linux 代理,并且该代理的源代码也可以在 GitHub 上找到,因此您可以在映像中安装它。微软甚至愿意在您遇到 Linux 问题时为您提供指导;只需购买支持计划!

对于一些商业 Linux 发行版,有很好的支持选项:

  • Red Hat:Microsoft 支持将帮助您使用 Azure 平台或服务,并且还将支持 Red Hat 内部的问题,但这需要一个支持计划。

  • Oracle Linux:Microsoft 提供支持计划;还可以从 Oracle 购买额外的商业支持。

  • SUSE:有 Microsoft 支持的高级映像;如果需要,他们会为您调用 SUSE。这个 SUSE 高级映像包括所有软件、更新和补丁。

  • 其他供应商:有 Microsoft 支持计划来覆盖其他供应商;您不必为此购买单独的计划。Microsoft 计划详情可在azure.microsoft.com/en-us/support/plans/上找到。

注意

请访问微软网站获取最新的认可发行版和版本列表,以及有关发行版可用支持的详细信息:

docs.microsoft.com/en-us/azure/virtual-machines/linux/endorsed-distros

部署 Linux 虚拟机

我们已经介绍了 Azure 中可用的 Linux 发行版以及您可以获得的支持水平。在上一节中,我们通过创建资源组和存储来设置了初始环境;现在是时候部署我们的第一个虚拟机了。

您的第一个虚拟机

资源组已创建,此资源组中已创建存储帐户,现在您可以在 Azure 中创建您的第一个 Linux 虚拟机了。

在 PowerShell 中,使用以下命令:

 New-AzVM -Name "UbuntuVM" -Location westus2 '
  -ResourceGroupName MyResource1 '
  -ImageName UbuntuLTS -Size Standard_B1S

该 cmdlet 将提示您为虚拟机提供用户名和密码:

在 Powershell 中为您的虚拟机提供用户名和密码

图 2.4:为您的虚拟机提供用户凭据

在 Bash 中,您可以使用以下命令:

az vm create --name UbuntuVM --resource-group MyResource2 \
  --image UbuntuLTS --authentication-type password \
  --admin-username student --size Standard_B1S

这非常简单,但是如果您以这种方式创建虚拟机实例,则可以设置的选项数量非常有限。 此过程将使用默认设置创建虚拟机所需的多个资源,例如磁盘、NIC 和公共 IP。

让我们深入了解一下细节,并获取有关所做选择的一些信息。

图像

在我们的示例中,我们部署了一个名为UbuntuLTS的图像的虚拟机。 您可以在几个 Linux 图像之间进行选择:

  • CentOS

  • Debian

  • RHEL

  • UbuntuLTS

  • CoreOS

  • openSUSE

  • SUSE Linux Enterprise

但是,不同供应商提供了许多更多的图像,称为发布商。

让我们获取这些发布商的列表。 在 PowerShell 中,使用此命令:

Get-AzVMImagePublisher -Location <REGION>

如您在以下截图中所见,Azure 有很多发布商,我们将从中选择一个进行演示:

Powershell 中各种图像发布商及其位置的列表

图 2.5:在 PowerShell 中列出图像发布商

在 Bash 中,您可以运行以下命令来获取发布商的列表:

az vm image list-publishers --location <REGION> --output table

列表是相同的:

Bash 中各种图像发布商及其位置的列表

图 2.6:在 Bash 中列出图像发布商

现在您知道发布商,可以使用以下命令获取发布商提供的图像列表:

Get-AzVMImageOffer -Location <REGION> '
  -PublisherName <PUBLISHER> | select offer

我们已经选择了Canonical作为发布商,现在我们正在尝试获取可用的优惠列表。 UbuntuServer是其中之一,我们将使用这个:

在 Powershell 中选择 Canonical 作为发布商并选择其优惠

图 2.7:列出 Canonical 发布商的优惠

或者,在 Azure CLI 中运行以下命令:

az vm image list-offers --location <REGION> '
  --publisher <PUBLISHER> --output table

输出是所谓的优惠列表。 优惠是由发布商创建的一组相关图像的名称。

现在我们需要知道图像的可用 SKU。 SKU 是指发行版的主要版本。 以下是使用 Ubuntu 的示例:

Get-AzVMImageSku -PublisherName <publisher> -Offer <offer>'
 -Location <location>

现在我们已经获得了发布商和优惠的值,让我们继续查看由Canonical发布的UbuntuServer可用的主要发行版(SKU):

Canonical 发布商为 UbuntuServer 提供的各种 SKU 的列表

图 2.8:在 Azure CLI 中列出由 Canonical 发布的 UbuntuServer 的 SKU

或者,在 Azure CLI 中运行以下命令:

az vm image list-skus --location <LOCATION> \
  --publisher <PUBLISHER> --offer <OFFER> -o table

查询此优惠中的特定实例:

Get-AzureVMImage -Location <REGION>'
 -PublisherName <PUBLISHER> -Offer <OFFER> '
 -Skus <SKU> | select Version -last 1 

让我们再次查看我们拥有的值。 因此,使用发布商名称、优惠和 SKU,我们将获取可用的版本。 在以下截图中,您可以看到图像版本19.10.201912170可用。 让我们为我们的虚拟机选择此图像:

在 Azure CLI 中使用发布商的名称、优惠和 SKU 获取可用图像的版本详细信息

图 2.9:在 Azure CLI 中选择可用的图像版本

这是在撰写本章时提供的最新版本。 如果有任何新版本发布,您可能会看到另一个版本号。

或者,在 Azure CLI 中使用以下命令:

az vm image list --location <REGION> --publisher <PUBLISHER> \
  --offer <OFFER> --sku <SKU> --all --query '[].version' \
  --output tsv | tail -1

为了将输出减少到最新版本,添加了参数以选择最后一行。 收集的信息包含Set-AzVMSourceImage cmdlet 的参数; 但是,在使用此命令之前,我们需要使用New-AzVMConfig创建一个新的虚拟机配置:

$vm = New-AzVmConfig -VMName <name> -VMSize "Standard_A1"
Set-AzVMSourceImage -PublisherName <PUBLISHER>'
  -Offer <OFFER> -Skus <SKU> -Version <VERSION>

最后,我们正在创建一个大小为Standard_A1的新虚拟机,并指示 PowerShell 使用Canonical发布的UbuntuServer优惠中的19_10-daily-gen2发行版的图像版本19.10.201912170

通过指示 Powershell 使用图像版本创建一个大小为 Standard_A1 的新虚拟机

图 2.10:创建一个 Standard_A1 大小的虚拟机

在 Bash 中,收集的信息包含az vm create命令的参数:

az vm create --name UbuntuVM2 --resource-group Packt-Testing-2   --image canonical:UbuntuServer:19_10-daily-gen2:19.10.201912170 --authentication-type password   --admin-username pacman --size Standard_B1S 

注意

在 Bash 和 PowerShell 中,可以使用单词latest代替特定版本。收集的信息不足以创建虚拟机。需要更多参数。

虚拟机大小

另一件您需要注意的事情是根据您的需求和成本决定虚拟机的大小。有关可用大小和定价的更多信息,请访问azure.microsoft.com/en-us/pricing/details/virtual-machines/linux

该网站上的列表(包括实例的价格)经常更改!您可以在命令行上获取列表(而不显示成本):

Get-AzVMSize -Location <REGION> | Format-Table
az vm list-sizes --location <REGION> -o table

一个小型虚拟机足以执行本书中的练习。在撰写本文时,Standard_B1ls是必要的基本性能。但最好重新检查虚拟机的大小/定价列表,如前面提到的。

在 PowerShell 中,New-AzVM cmdlet 可以使用-size参数,或者可以在New-AzVMConfig cmdlet 中使用它:

New-AzVMConfig -VMName "<VM NAME>" -VMSize <SIZE>

在 Bash 中,添加az vm create命令的--size参数。

虚拟机网络

Azure 虚拟网络允许虚拟机、互联网和其他 Azure 服务之间通过安全网络进行通信。当我们在本章开头创建第一个虚拟机时,有关网络的几个项目是自动创建的:

  • 虚拟网络

  • 虚拟子网

  • 附加到虚拟机并插入虚拟网络的虚拟网络接口

  • 配置在虚拟网络接口上的私有 IP 地址

  • 公共 IP 地址

网络资源将在第四章,管理 Azure中介绍;目前,我们只会查询虚拟机的私有和公共 IP 地址。使用此命令获取公共 IP 地址列表:

Get-AzPublicIpAddress -ResourceGroupName <RESOURCE GROUP>'
 | select Name,IpAddress

要获取所有虚拟机的私有 IP 地址列表,请使用以下命令:

Get-AzNetworkInterface -ResourceGroupName <resource group name> | ForEach { $interface = $_.Name; $ip = $_ | Get-AzNetworkInterfaceIpConfig | Select PrivateIPAddress; Write-Host $interface $ip.PrivateIPAddress }

前面的命令可能看起来有点复杂,但这是一个方便的脚本,用于获取私有 IP 列表。如果您想要获取资源组中虚拟机的私有 IP 地址,可以使用以下命令:

Get-AzNetworkInterface -ResourceGroup <resource group name>

获取的输出将以 JSON 格式显示,并且您可以在IpConfigurations下看到私有 IP 地址:

以 JSON 格式显示资源组中虚拟机的私有 IP 地址的输出

图 2.11:资源组中虚拟机的私有 IP 地址

这也可以使用 Azure CLI 来完成。要获取虚拟机的私有 IP 地址列表,请使用以下命令:

az vm list-ip-addresses --resource <RESOURCE GROUP> --output table

公共 IP 地址是使虚拟机通过互联网访问的 IP 地址。进入此 IP 地址的虚拟机网络流量经历网络地址转换NAT)以配置在 Linux 虚拟机的网络接口上的私有 IP 地址。

虚拟机信息

虚拟机部署后,可以使用 PowerShell 和 Bash 获取附加到虚拟机的所有信息,例如状态。查询状态很重要;有几种状态:

  • 运行中

  • 已停止

  • 失败

  • 已停止

如果虚拟机未停止,Microsoft 将向您收费。Failed状态表示虚拟机无法启动。要查询状态,请执行以下命令:

Get-AzVM -Name <VM NAME> -Status -ResourceGroupName <RESOURCE GROUP>

在 Bash 中,可以接收部署的虚拟机的状态,但如果需要将输出缩小到单个实例,则无法使用复杂的查询:

az vm list --output table

要停止虚拟机,首先停止它:

Stop-AzVM -ResourceGroupName <RESOURCE GROUP> -Name <VM NAME>

现在您可以将其停止:

az vm deallocate --name <VM NAME> --resource-group <RESOURCE GROUP>

您可以获取有关部署的虚拟机的更多信息。在 PowerShell 中,很难接收虚拟机的属性。首先,创建一个变量:

$MYVM=Get-AzVM -Name <VM NAME> -ResourceGroupName <RESOURCE GROUP>

现在要求此MYVM对象的属性和方法:

$MYVM | Get-Members 

查看HardwareProfile属性以查看此实例的大小:

$MYVM.HardwareProfile

或者,为了更精确地查看虚拟机信息,使用以下命令:

$MYVM.HardwareProfile | Select-Object -ExpandProperty VmSize

你也可以尝试NetworkProfileOSProfileStorageProfile.ImageReference

如果你想在 Bash 中使用az命令,你可能想尝试的第一个命令是这个:

az vm list –-resource-group <RESOURCE GROUP>

唯一的问题是它同时显示了所有虚拟机的所有信息;幸运的是,也有一个show命令,可以将输出减少到单个虚拟机:

az vm show --name <VM NAME> --resource-group <RESOURCE GROUP>

并且最好通过使用查询来限制输出。例如,如果你想查看特定虚拟机的存储配置文件,可以查询如下:

az vm show --name <VM NAME> --resource-group <RESOURCE GROUP>\
  --query 'storageProfile'

上述命令应该给出以下输出:

查看 SUSE 虚拟机存储配置文件的命令

图 2.12:SUSE 虚拟机的存储配置文件

连接到 Linux

虚拟机正在运行,准备让您远程登录,使用您在部署第一台虚拟机时提供的凭据(用户名和密码)。另一种更安全的连接到 Linux 虚拟机的方法是使用 SSH 密钥对。由于其复杂性和长度,SSH 密钥更安全。此外,Azure 上的 Linux 支持使用Azure 活动目录Azure AD)进行登录,用户将能够使用其 AD 凭据进行身份验证。

使用密码身份验证登录到您的 Linux 虚拟机

虚拟机网络部分,查询了虚拟机的公共 IP 地址。我们将使用这个公共 IP 通过本地安装的 SSH 客户端连接到虚拟机。

SSH,或安全外壳,是一种加密的网络协议,用于管理和与服务器通信。Linux、macOS、Windows 子系统WSL)和最近更新的 Windows 10 都配备了基于命令行的 OpenSSH 客户端,但也有更高级的客户端可用。以下是一些示例:

  • Windows:PuTTY、MobaXterm 和 Bitvise Tunnelier

  • Linux:PuTTY、Remmina 和 Pac Manager

  • macOS:PuTTY、Termius 和 RBrowser

使用 OpenSSH 命令行客户端连接到虚拟机:

ssh <username>@<public ip>

使用 SSH 私钥登录到您的 Linux 虚拟机

使用用户名和密码不是登录远程机器的最佳方式。这不是完全不安全的操作,但你仍然在连接中发送你的用户名和密码。如果你想远程执行脚本、执行备份操作等,这也很难使用。

另一种更安全的登录系统的方法是使用 SSH 密钥对。这是一对两个密码安全的密钥:私钥和公钥。

私钥由客户端保留,不应复制到任何其他计算机。它应该绝对保密。在创建密钥对时,最好用密码保护私钥。

另一方面,公钥可以复制到您想要管理的所有远程计算机上。这个公钥用于加密只有私钥才能解密的消息。当您尝试登录时,服务器通过使用密钥的这个属性来验证客户端拥有私钥。没有密码发送到连接中。

有多种方法可以创建 SSH 密钥对;例如,PuTTY 和 MobaXterm 都提供了创建工具。你必须在每台需要访问远程机器的工作站上执行此操作。在本书中,我们使用ssh-keygen,因为它适用于每个操作系统:

ssh-keygen

上述命令的输出应该如下所示:

使用 ssh-keygen 命令创建 SSH 密钥对

图 2.13:使用 ssh-keygen 创建 SSH 密钥对

不要忘记输入密码!

要了解如何使用 SSH 密钥对访问您的虚拟机,让我们创建一个新的虚拟机。如果您还记得,当我们之前创建 Linux 机器时,我们使用了az vm create命令和authentication-type作为密码,但在下面的命令中,我们使用了--generate-ssh-keys参数。这将生成一个 SSH 密钥对,并将其添加到您的主目录中的.ssh目录中,可用于访问虚拟机:

az vm create --name UbuntuVM3 --resource-group MyResource2 \
  --admin-username student --generate-ssh-keys --image UbuntuLTS

如果您想在 PowerShell 中执行此操作,请使用Add-AzVMSshPublicKey cmdlet。有关该命令的更多信息,请参阅docs.microsoft.com/en-us/powershell/module/azurerm.compute/add-azurermvmsshpublickey?view=azurermps-6.13.0

虚拟机创建后,您将能够使用此命令访问它:

ssh student@<IP ADDRESS>

总结

本章介绍了 Microsoft Azure 的第一步。第一步始终涉及创建一个新帐户或使用现有的公司帐户。有了帐户,您就可以登录并开始发现 Azure 云。

在本章中,使用 Azure CLI 命令az或通过 PowerShell 发现了 Azure 云;通过示例,您了解了以下内容:

  • Azure 登录过程

  • 区域

  • 存储帐户

  • 由出版商提供的图像

  • 虚拟机的创建

  • 查询附加到虚拟机的信息

  • Linux 是什么以及 Linux 虚拟机的支持

  • 使用 SSH 和 SSH 密钥对访问 Linux 虚拟机

下一章从这里开始,带着一个新的旅程:Linux 操作系统。

问题

  1. 使用命令行访问 Microsoft Azure 的优势是什么?

  2. 存储帐户的目的是什么?

  3. 您是否能想到为什么会收到以下错误消息?

Code=StorageAccountAlreadyTaken
Message=The storage account named mystorage is already taken.
  1. 提供的报价和图像之间有什么区别?

  2. 停止和取消分配虚拟机之间有什么区别?

  3. 使用私有 SSH 密钥进行身份验证访问 Linux 虚拟机的优势是什么?

  4. az vm create命令有一个--generate-ssh-keys参数。创建了哪些密钥,它们存储在哪里?

进一步阅读

这一章绝不是关于使用 PowerShell 的教程。但是,如果您想更好地理解示例,或者想更多地了解 PowerShell,我们建议您阅读 Packt Publishing 的Mastering Windows PowerShell Scripting – Second Edition(ISBN:9781787126305)。我们建议您从第二章使用 PowerShell开始,并至少继续到第四章在 PowerShell 中使用对象

您可以在网上找到大量关于使用 SSH 的文档。一个很好的起点是这本 wikibook:en.wikibooks.org/wiki/OpenSSH

如果您希望更多地了解 Linux 管理,Packt Publishing 的Linux Administration Cookbook是一个很好的资源,特别是对于系统工程师。

要深入了解安全性和管理任务,这是一个很好的阅读材料:Mastering Linux Security and Hardening,作者是 Donald A. Tevault,由 Packt Publishing 出版。

第三章:基本的 Linux 管理

在部署了你的第一个 Linux 虚拟机(VM)之后,让我们登录,讨论一些基本的 Linux 命令,并学习如何在 Linux 环境中找到我们的方法。本章是关于基本的 Linux 管理,从 Linux shell 开始,用于与 Linux 系统交互。我们将讨论如何使用 shell 来完成我们的日常管理任务,比如访问文件系统,管理进程(如启动和终止程序)等等。

在本章的最后部分,我们将讨论自主访问控制(DAC)模型以及如何在 Linux 中创建、管理和验证用户和组,并根据用户名和组成员身份获取文件和目录的权限。我们还将涵盖更改文件所有权以及更改和验证基本权限和访问控制列表。

以下是本章的主要主题:

  • 与 shell 交互和配置 shell

  • 使用 man 页面获取帮助

  • 通过 shell 处理和编辑文本文件

  • 理解文件层次结构,管理文件系统和挂载新文件系统

  • 管理进程

  • 用户和组管理

Linux Shell

在上一章中,我们创建了 VM 并使用 SSH 登录,但是我们如何与 Linux 机器交互并指示其执行任务呢?正如我们在本章开头提到的,我们将使用 shell。

我们将探索广泛使用的 Bash shell,配置 Bash shell 以及如何使用它。shell 是一个用户界面,您可以在其中执行以下操作:

  • 与内核、文件系统和进程交互

  • 执行程序、别名和 shell 内置

shell 提供以下功能:

  • 脚本编写

  • 自动补全

  • 历史和别名

有许多不同的 shell 可用,例如 KornShell、Bash 和 Z shell(Zsh)。Bash 是几乎每个 Linux 系统上的默认 shell。它的开发始于 1988 年,作为最古老的 shell 之一 Bourne shell 的替代品。Bash 基于 Bourne shell 和从其他 shell(如 KornShell 和 C shell)中学到的经验教训。Bash 已成为最流行的 shell,并可在许多不同的操作系统上使用,包括 Windows 10、FreeBSD、macOS 和 Linux。

以下是添加到 Bash 版本 2.05a(2001 年发布)中的一些最重要的功能,这些功能使 Bash 成为最突出的 shell:

  • 命令行编辑

  • 历史支持

  • 自动补全

  • 整数计算

  • 函数声明

  • 文档(一种将文本输入到单独文件中的方法)

  • 新变量,如$RANDOM$PPID

最近,Z shell 变得越来越受欢迎;这个 shell 的开发始于 1990 年,可以看作是对 Bash 的扩展。它还具有与 Bash 的兼容模式。它具有更好的自动补全支持,包括自动更正和更高级的路径名扩展。其功能可以通过模块进行扩展,例如,以获取更多关于命令的帮助。值得一提的是 Oh-My-ZSH(https://github.com/robbyrussell/oh-my-zsh)和 Prezto(https://github.com/sorin-ionescu/prezto)项目:它们提供主题、高级配置和插件管理,使 Z shell 非常用户友好。所有这些好功能都是有代价的:Z shell 肯定比 Bash 更耗资源。

执行命令

shell 的最重要功能之一是可以执行命令。命令可以是以下之一:

  • Shell 内置(由相关 shell 提供的命令)

  • 在文件系统上可执行

  • 别名

要找出正在执行的命令类型,可以使用type命令:

type echo

添加-a参数将显示包含可执行文件echo的所有位置。在下面的截图中,我们可以看到当我们添加-a参数时,由于可执行文件的存在,shell 给出了对/usr/bin/echo目录的引用:

使用 type 命令和参数-a 来查找名为 echo 的可执行文件的类型和位置。

图 3.1:包含可执行文件 echo 的位置

让我们对ls做同样的操作:

type ls

所以,你将得到一个类似的输出type ls

运行命令 type -a ls 来显示包含可执行文件 ls 的位置。通过运行这个命令,我们也可以看到 ls 是 ls --color=auto 的别名

图 3.2:包含可执行文件 ls 的位置

在这里,我们可以看到lsls --color=auto命令的别名,添加了一些参数。别名可以替换现有命令或创建新命令。没有参数的alias命令会给出已经配置的别名:

在不同的关键词上运行别名命令,以显示这些命令已经配置的别名。

图 3.3:使用别名命令

ll别名是一个新创建命令的例子。mv命令是一个替换的例子。使用以下命令创建一个新的别名:

alias <command>='command to execute'

例如,要用search替换grep命令,执行以下命令:

alias search=grep

你创建的别名将被添加到.bashrc文件中。如果你想要移除一个创建的别名,可以使用unalias命令:

unalias <alias name>

如果你想要移除所有定义的别名,可以使用unalias -a

which命令标识了$PATH变量中程序的位置。这个变量包含了一个目录列表,用于查找可执行文件。这样,你就不必提供完整的路径:

which passwd

输出告诉你它在/usr/bin目录中可用:

使用 which 命令识别程序的位置。

图 3.4:$PATH 变量中程序的目录位置

命令行编辑

在许多方面,输入 Bash shell 中的命令与在文本编辑器中工作是一样的。这可能是为什么有一些快捷键,比如跳转到行首,而且这些快捷键与两个最著名、最常用的文本编辑器 Emacs 和 vi 中的快捷键是一样的。

默认情况下,Bash 被配置为处于 Emacs 编辑模式。如果你想要检查当前的编辑模式,运行set -o。输出将告诉你 Emacs 或 vi 是否被设置为on。以下是一些非常重要的快捷键:

列出了一些重要的 Bash shell 导航快捷键。

图 3.5:Bash shell 快捷键列表

如果你想使用 vi 模式,执行以下命令:

set -o vi

要切换回 Emacs 模式,使用以下命令:

set -o emacs

注意

vi 编辑器在本章的后面部分使用文本文件中有介绍。现在,你可以在命令模式下使用几乎每个命令,包括导航复制粘贴

set命令是 Bash 内置命令,用于切换特定于 Bash 的属性。没有参数时,它会显示环境变量。

处理历史记录

Bash shell 提供了命令行工具,你可以用来处理用户的命令历史。你执行的每个命令都会在主目录的历史文件~/.bash_history中注册。要查看这个历史的内容,执行以下命令:

history

输出显示了一个已使用命令的编号列表;你可以简单地使用以下命令重做一个命令:

  • !<number>:根据历史列表编号执行命令。

  • !<-number>:例如,!-2执行了在历史记录中比上一条命令早两条的命令。

  • !<first characters of the command>: 这将执行以这个字符开头的最后一个项目。

  • !!: 重复执行上一条命令。您可以将其与其他命令结合使用。例如,sudo !!

您可以使用Ctrl + R(Emacs 模式)或正斜杠(vi 命令模式)向后搜索历史记录。可以使用箭头键进行浏览。

历史文件不是在执行命令后直接编写的,而是在登录会话结束时。如果您在多个会话中工作,直接编写历史记录可能是一个好主意。要执行此操作,请执行以下操作:

history -a

要在另一个会话中读取刚保存的历史记录,请执行以下命令:

history -r

要清除当前会话的历史记录,请使用以下命令:

history -c

如果要将历史记录保存到文件中,可以执行以下命令:

history -w <filename>

因此,通过保存清除的历史记录,您清空了历史文件。

与历史记录一起工作的另一个好功能是您可以编辑它。假设您执行了ls -alh命令,但您需要ls -ltr。只需输入:

^alh^ltr

这实际上与以下内容相同:

!!:s/ltr/alh/

当然,您可以对历史记录中的每个条目执行此操作;例如,对于历史记录列表中的第 6 个条目,请使用:

!6:s/string/newstring/

有时您需要更灵活,想要编辑包含许多拼写错误的大行。输入fc命令。使用以下命令修复命令:

fc <history number> 

这将打开一个文本编辑器(默认为 vi),保存修改后,将执行修改后的命令。

自动补全

每个人都会犯错;没有人能记住每个参数。自动补全可以防止许多错误,并在输入命令时以多种方式帮助您。

自动补全适用于以下情况:

  • 可执行文件

  • 别名

  • Shell 内置

  • 文件系统上的程序

  • 文件名

  • 参数,如果实用程序支持并且安装了bash-completion软件包

  • 变量

如果 shell 配置为 Emacs 模式,请使用Ctrl + I来激活自动完成;如果 shell 配置为 vi 模式,您也可以使用Ctrl + P

注意

如果有多个可能性,您必须两次按Ctrl + ICtrl + P

Globbing

Globbing 是将 Linux shell 中包含通配符的非特定文件名扩展为一个或多个特定文件名的过程。Globbing 的另一个常用名称是路径名扩展。

Bash shell 中识别以下通配符:

  • ?:一个字符。

  • *:多个字符。请注意,如果将此通配符用作第一个字符,则以点开头的文件名将不匹配。当然,您可以使用.*

  • [a-z], [abc]:来自范围的一个字符。

  • {a,b,c}:a 或 b 或 c。

以下是使用通配符的一些不错的例子:

  • echo *:这将列出当前工作目录中的文件或目录。

  • cd /usr/share/doc/wget*:这将切换到以wget开头的目录名所在的/usr/share/doc目录。

  • ls /etc/*/*conf:这将列出/etc目录下所有目录中的所有.conf文件。以下是此命令的示例:

运行命令 ls /etc/*/*conf 以显示/etc 目录下所有.conf 文件的图像。

图 3.6:列出所有目录中的.conf 文件
  • mkdir -p /srv/www/{html,cgi-bin,logs}:这将在/srv/www目录下创建htmlcgi-binlog目录。

重定向

在 Unix 早期,开发人员之一 Ken Thompson 定义了Unix 哲学,这是一种基于经验的方法,旨在使一切尽可能模块化,并尽可能重用代码和程序。特别是在那些日子里,可重用性对性能原因很重要,并且提供了一种允许轻松维护代码的方法。

在 Peter H Salus 修改的Unix 哲学版本中,重定向的目标如下:

  • 编写只做一件事并且做得很好的程序。

  • 编写程序以协同工作。

  • 编写处理文本流的程序,因为这是一个通用接口。

为了使这种哲学成为可能,开发了支持文件描述符或现代术语中的通信通道的程序。每个程序至少有三个通信通道:

  • 标准输入(0)

  • 标准输出(1)

  • 标准错误(2)

此实现的一个很好的特性是您可以重定向通道。

将标准输出重定向到文件,使用以下命令:

command > filename

要将标准输出重定向并追加到现有文件中,请使用:

command >> filename

将标准错误和输出重定向到文件,如下所示:

command &> filename 

首先将标准输出重定向到文件,然后也将标准错误重定向到那里,使用:

command 2>&1 filename

要重定向标准输入,请使用以下命令:

filename < command

让我们进行一个活动,以帮助我们理解重定向的概念。请先运行命令,验证输出,然后使用以下方法将其重定向到文件。例如,运行ls并验证输出,然后使用>将输出重定向到/tmp/test.list。您始终可以使用cat /tmp/test.list检查文件:

ls > /tmp/test.list 

echo hello > /tmp/echotest 

echo hallo again >> /tmp/echotest 

ls -R /proc 2> /tmp/proc-error.test 

ls -R /proc &> /tmp/proc-all.test 

sort < /etc/services

输入重定向的特殊版本是heredoc.txt

cat << EOF >> /tmp/heredoc.txt 
 this is a line 
 this is another line 
EOF

cat命令将标准输出连接并将其附加到/tmp/heredoc.txt文件中。由于键盘直到遇到标签(在本例中为EOF)之前不是标准输入,因此无法中断或中断命令。这种方法通常用于从脚本创建配置文件。

另一种可能性是使用|符号将一个命令的标准输出重定向到另一个命令的标准输入:

command | other command

例如:

ls | more

使用tee命令,您可以结合重定向和管道的功能。有时您希望确保command 1的输出被写入文件以进行故障排除或记录,并且同时将其管道传输到另一个命令的标准输入:

command 1 | tee file.txt | command 2

还可以使用-a参数将内容追加到文件中。

tee的另一个用例是:

<command> | sudo tee <file> 

这样,就可以在不使用复杂的su结构的情况下写入文件。

使用变量

每个命令行界面,即使没有高级脚本编写的可能性,也都有变量的概念。在 Bash 中,有两种类型的变量:

  • 影响 Bash 行为或提供有关 Bash 的信息的内置或内部变量。一些示例包括BASH_VERSIONEDITORPATH

  • 已知一个或多个应用程序的环境变量,包括内置变量和用户定义变量。

要列出当前 shell 的环境变量,可以使用envprintenv命令。printenv还能够显示特定变量的内容:

运行命令 printenv PATH 以列出当前 shell 的环境变量。

图 3.7:使用 printenv 命令显示特定变量的内容

查看变量内容的另一种方法如下:

echo $VARNAME

要声明环境变量,请执行var=value。例如:

animal=cat 

echo $animal

要向值添加更多字符,请使用:

animal=$animal,dog 

echo $animal

animal变量只对当前 shell 可见。如果要将其导出到子进程,需要导出该变量:

export animal

Bash 还能够进行简单的计算:

a=$(( 4 + 2 ))

或者,您可以使用此命令:

let a=4+2 
echo $a 

另一个特性是将命令的输出放入变量中-一种称为嵌套的技术:

MYDATE=$(date +"%F")
echo $MYDATE

当然,这只是 Bash 能够做到的一小部分,但这应该足够让您学会如何处理 Bash 配置文件并根据需要修改它们,以使它们以您想要的方式运行。

Bash 配置文件

Bash shell 有三个重要的系统范围配置文件:/etc/profile/etc/bashrc/etc/environment。这些文件的目的是存储关于您的 shell 的信息,如颜色、别名和变量。例如,在前一节中,我们添加了一些别名,它们存储在一个名为bashrc的文件中,这是一个配置文件。每个文件都有自己的目的;我们现在将逐个查看它们。

/etc/profile是一个在用户登录到系统时执行的脚本。修改此文件不是一个好主意;而是使用快捷方式/etc/profile.d目录。该目录中的文件按字母顺序执行,并且必须具有.sh作为文件扩展名。作为一个附注,/etc/profile不仅被 Bash shell 使用,而且被所有 Linux 的 shell 使用,除了 PowerShell。您还可以在主目录中创建一个特定于用户的配置文件脚本,~/.bash_profile,这也是特定于 Bash 的。

配置文件脚本的一些典型内容如下:

set -o vi  
alias man="pinfo -m" 
alias ll="ls -lv --group-directories-first" 
shopt -u mailwarn  
unset MAILCHECK

注意

如果您使用 Ubuntu 或类似的发行版,默认情况下不会安装pinfo。运行apt install pinfo来安装它。

shopt命令更改了一些默认的 Bash 行为,比如检查邮件或 globbing 的行为。unset命令是set命令的相反。在我们的示例中,默认情况下,Bash 每分钟检查一次邮件;执行unset MAILCHECK命令后,MAILCHECK变量被移除。

/etc/bashrc脚本在任何用户调用 shell 或 shell 脚本时都会启动。出于性能原因,尽量保持它尽可能简洁。您可以使用特定于用户的~/.bashrc文件,如果退出 shell,则会执行~/.bash_logout脚本。bashrc配置文件通常用于修改提示符(PS1变量):

DARKGRAY='\e[1;30m'
GREEN='\e[32m'
YELLOW='\e[1;33m'
PS1="\n$GREEN[\w] \n$DARKGRAY(\t$DARKGRAY)-(\u$DARKGRAY)-($YELLOW-> \e[m"

让我们看看PS1变量的参数:

  • 颜色(比如传递给 PS1 变量的 GREEN、DARKGRAY)是用 ANSI 颜色代码定义的。

  • \e:ANSI 中的转义字符。

  • \n:换行。

  • \w:当前工作目录。

  • \t:当前时间。

  • \u:用户名。

/etc/environment文件(在基于 Red Hat 的发行版中默认为空)是在登录时执行的第一个文件。它包含每个进程的变量,而不仅仅是 shell。它不是脚本,每行只有一个变量。

以下是/etc/environment的示例:

EDITOR=/usr/bin/vim
BROWSER=/usr/bin/elinks
LANG=en_US.utf-8
LC_ALL=en_US.utf-8
LESSCHARSET=utf-8
SYSTEMD_PAGER=/usr/bin/more

EDITOR变量是一个重要的变量。许多程序可以调用编辑器;有时默认是 vi,有时不是。设置默认值可以确保您始终可以使用您喜欢的编辑器。

注意

如果您不想注销并重新登录,可以使用source命令,例如source /etc/environment。这样,变量将被读入当前的 shell。

获取帮助

无论您是 Linux 的新手还是长期用户,都会时不时需要帮助。不可能记住所有命令及其参数。几乎每个命令都有一个--help参数,有时在/usr/share/doc目录中安装了文档,但最重要的信息来源是信息文档和 man 页面。

使用 man 页面

有一句话,阅读完整的手册RTFM),有时人们会用另一个不太友好的词替换fine。几乎每个命令都有一个手册:man 页面为您提供了所有需要的信息。是的,不是所有的 man 页面都容易阅读,特别是旧的页面,但如果您经常使用 man 页面,您会习惯它们,并且能够快速找到所需的信息。通常,man 页面已安装在您的系统上,并且可以在线获取:man7.org/linux/man-pages

请注意,Azure 镜像中的 openSUSE Leap 和 SUSE Linux Enterprise Server 中删除了 man 页面。您必须重新安装每个软件包才能再次使用它们:

sudo zypper refresh
for package in $(rpm -qa);
  do sudo zypper install --force --no-confirm $package;
done

man 页面被安装在/usr/share/man目录中,以 GZIP 压缩的存档形式。man 页面是特别格式化的文本文件,您可以使用man命令或pinfo来阅读。pinfo实用程序充当文本浏览器,非常类似于基于文本的网络浏览器。它添加了超链接支持和使用箭头键在不同的 man 页面之间导航的能力。

注意

如果您想要用pinfo替换man命令,最好使用alias man="pinfo -m"命令创建一个别名。

所有的 man 页面都遵循相似的结构,它们总是被格式化并分成各个部分:

  • 名称:命令的名称和简要解释。通常是一行;详细信息可以在 man 页面的描述部分找到。

  • 概要:包含所有可用参数的概述。

  • ifconfig命令明确说明这个命令已经过时。

  • 选项:命令的所有可用参数,有时包括示例。

  • 示例:如果示例不在选项部分,可能会有一个单独的部分。

  • 文件:对于这个命令很重要的文件和目录。

  • 另请参阅:指的是其他 man 页面、info 页面和其他文档来源。一些 man 页面包含其他部分,如注释、错误、历史、作者和许可证。

Man 页面是帮助页面,分为几个部分;这些部分在 man 页面的描述部分中描述。您可以使用man man来了解更多关于这些部分的信息。以下屏幕截图显示了不同的部分:

列出 man 页面的各个部分的屏幕截图。

图 3.8:man 页面的不同部分

了解这些分区是很重要的,特别是如果您想要搜索文档。要能够搜索文档,您需要索引 man 页面:

sudo mandb 

注意

通常,在安装软件包后,索引会自动更新。有时,打包者可能没有添加一个后安装脚本来执行mandb命令。如果您找不到信息并且非常确定应该有一个 man 页面,手动执行该命令是个好主意。

之后,您可以使用aproposman -k命令来找到您需要的信息。无论您选择哪个,语法都是一样的:

man -k -s 5 "time"

在前面的命令中,我们搜索了单词time,将搜索限制在 man 页面的第五部分。

使用 info 文档

Info 文档是另一个重要的信息来源。man 页面和 info 页面的区别在于 info 页面的格式更自由,而 man 页面是某个命令的一种说明手册。Info 文档大多数时候是完整的手册。

Info 文档和 man 页面一样,被压缩并安装在/usr/share/info目录中。要阅读它们,您可以使用info或更现代的pinfo。这两个命令都是文本浏览器。如果您是 Emacs 编辑器的忠实粉丝,您可以使用 InfoMode (www.emacswiki.org/emacs/InfoMode)来阅读 info 文档。

其中一个很好的功能是,您可以使用pinfoinfo直接跳转到文档中的超链接:

pinfo '(pinfo) Keybindings'

注意

如果您使用 Ubuntu 或类似的发行版,默认情况下不会安装pinfo。运行apt install pinfo来安装它。

前面的例子打开了pinfo的 man 页面,并直接跳转到Keybindings部分。

pinfo命令有一个搜索选项,-a。如果有匹配,它将自动打开相应的info文档或 man 页面。例如,如果您想了解echo命令,使用pinfo -a echo;它会带您到echo命令的帮助部分。

info命令也有一个搜索选项:-k。使用-kinfo命令将在所有可用手册中查找关键字。例如,在这里我们检查了paste关键字,它返回了所有可能的匹配项:

使用 info -k 命令在所有可用手册中查找关键字 paste。

图 3.9:使用 info 命令检查 paste 关键字

其他文档

另一个文档来源是 Linux 发行版供应商提供的文档。Red Hat、SUSE、Canonical 和 Debian 的网站托管了有用的手册、维基等。它们可能非常有用,特别是对于特定于发行版的主题,如软件管理。

有两个不是微软认可的发行版,Gentoo 和 Arch Linux,它们在其网站上有出色的维基。当然,这些维基中的一些信息是特定于这些发行版的,但许多文章是有用的,并且适用于每个发行版。

Linux 基金会在wiki.linuxfoundation.org上托管了一个维基,其中包含有关诸如网络等主题的文档,以及init系统、systemd 和 Linux 防火墙(firewalld)等标准;这些主题在第五章,高级 Linux 管理中讨论。

最后,Linux 文档项目可以在www.tldp.org找到。尽管你可以在那里找到的许多文档都非常古老,但它仍然是一个很好的起点。

处理文本文件

由 Ken Thompson 发起的 Unix 哲学旨在创建一个占用空间小、用户界面清晰的功能强大的操作系统。因为 Unix 哲学的一部分是编写处理文本流的程序,因为那是一个通用接口,程序之间的通信、配置文件和许多其他内容都是以纯文本实现的。本节是关于处理纯文本的。

阅读文本

在最基本的层面上,以纯文本格式阅读文件的内容意味着将该文件的内容重定向到标准输出。cat命令就是可以做到这一点的实用程序之一——将一个或多个文件(或另一个输入通道)的内容连接到标准输出:

使用 cat 实用程序读取文件/etc/shells 的内容。

图 3.10:使用 cat 命令生成标准输出

该实用程序的一些不错的参数包括:

  • -A:显示所有不可打印字符

  • -b:编号行,包括空行

  • -n:编号行,不包括空行

  • -s:抑制重复(!)空白行

还有另一个类似于cat的实用程序,即tac实用程序。这将以逆序打印文件:

通过运行 tac 实用程序以逆序打印文件的内容。

图 3.11:使用 tac 实用程序以逆序打印文件

cat命令的问题在于它只是将内容转储到标准输出而不对内容进行分页,并且终端的回滚功能不是很好。

more实用程序是一个分页过滤器。它一次显示一个屏幕的文本,并提供一个基本的搜索引擎,可以通过使用正斜杠来激活。在文件末尾,more将退出,有或没有消息按空格键继续

less实用程序比more实用程序更先进。它具有以下功能:

  • 能够向前、向后和水平滚动

  • 高级导航

  • 高级搜索引擎

  • 多文件处理

  • 能够显示有关文件的信息,如文件名和长度

  • 能够调用 shell 命令

moreless中,v命令允许我们切换到编辑器,默认为 vi 编辑器。

注意

moreless都可以在每个发行版上使用;但是,在某些发行版上,moreless的别名。使用type命令进行验证!

如果您只想看到文件顶部的特定行数,有一个名为head的实用程序。默认情况下,它显示文件的前 10 行。您可以使用-n参数修改此行为,以便指定行数,使用-c参数指定字节/千字节的数量。

head实用程序是tail的相反;它默认显示前 10 行。例如,我们有一个名为states.txt的文件,其中按字母顺序列出了美国各州的名称。如果我们使用head命令,它将打印文件的前 10 行,如果我们使用tail命令,它将打印最后 10 行。让我们来看一下这个:

使用 head 和 tail 实用程序打印文件的前 10 个和最后 10 个条目。

图 3.12:使用 head 和 tail 实用程序列出文件的前 10 个和最后 10 个条目

它识别与head相同的参数以修改其行为。但是有一个额外的参数,使得这个实用程序对于日志记录非常有用。-f在文件增长时追加输出;这是一种跟踪和监视文件内容的方法。一个非常著名的例子是:

sudo tail -f /var/log/messages

在文本文件中搜索

您可能听说过 Linux 中的一切都是文件。此外,Linux 中的许多东西都是由文本流和文本文件管理的。迟早,您会想要搜索文本以进行修改。这可以通过使用正则表达式来实现。正则表达式(简称 regex)是一种特殊字符和文本的模式,用于在执行搜索时匹配字符串。正则表达式被许多应用程序使用,这些应用程序具有内置处理器,例如 Emacs 和 vi 文本编辑器,以及grepawksed等实用程序。许多脚本和编程语言都支持正则表达式。

在本书中,我们只会涵盖这个主题的基础知识——足够让您在日常系统管理任务中使用它们。

每个正则表达式都是围绕一个原子构建的。原子标识要匹配的文本以及在进行搜索时要找到的位置。它可以是已知的单个字符项(或者如果您不知道字符,则是一个点),一个类,或者一个范围,比如:

一个表格,显示如何使用正则表达式来表示多个单个字符项和范围。

图 3.13:原子的示例

正则表达式也可以以简写类的形式表示。以下是一些简写类的示例:

以简写类的形式表示不同正则表达式的列表。

图 3.14:简写类的示例

我们可以使用位置锚点来确定下一个字符的位置。一些常用的位置锚点包括:

重要位置锚点列表,用于确定下一个字符的位置。

图 3.15:位置锚点列表

使用重复运算符,您可以指定字符应该出现多少次:

重复运算符列表

图 3.16:重复运算符列表

一些例子如下:

  • 如果搜索字符b并找到单词boom,它将匹配字母b。如果搜索bo,它将按照这个顺序匹配这些字符。

  • 如果您搜索bo{,2}m,单词bomboom将匹配。但如果存在单词booom,它将不匹配。

  • 如果您搜索^bo{,2}m,只有当单词boom位于行的开头时才会匹配。

可以使用以下内容找到正则表达式的参考:

man 7 regex

我们已经提到的一个实用程序是grep实用程序,它用于在文本文件中进行搜索。这个实用程序有多个版本;如今,egrep是最常用的版本,因为它具有最完整的正则表达式支持,包括简写范围和 OR 交替运算符|

egrepgrep的常见选项包括:

列出了 egrep 和 grep 的常见选项的表格。

图 3.17:egrep 和 grep 选项

您还可以通过查看 man 页面来查看其他选项。

这是grep的一个简单示例:

grep 示例

图 3.18:grep 示例

另一个非常有用的实用程序是awk。现在,awk是由开发人员 Alfred Aho、Peter Weinberger 和 Brian Kernighan 创建的实用程序。它是用于生成和操作日志文件或报告的文本文件的脚本语言。awk不需要任何编译,您可以在报告中提及所需的字段。

让我们看一个例子:

awk -F: '/^root/ {print "Homedir of root:", $6}' /etc/passwd

它扫描/etc/passwd文件,并使用字段分隔符冒号来拆分内容。它搜索以root字符串开头的行,并打印一些文本(root 的主目录:)和第六列。

编辑文本文件

由于文本文件在 Linux 中非常重要,因此文本编辑器非常重要。每个发行版都在其存储库中提供一个或多个编辑器,用于图形和非图形环境。您可以肯定至少有 vim(现代 vi 实现)和 Emacs 可用。vi 爱好者和 Emacs 爱好者之间一直存在着一场战争——他们已经互相侮辱了几十年,并将在未来的几十年内继续这样做。

我们不会为您做决定;相反,如果您已经熟悉其中一个,请坚持下去。如果您不了解 vi 或 Emacs,请尝试一段时间并自行决定。

还有一些其他可用的编辑器:

  • nano,专有 Pico 的免费克隆,Pico 是 Pine 电子邮件客户端的文本编辑器组件

  • mceditMidnight CommanderMC)文件管理器的一部分,可以独立运行

  • joe,它可以模拟 nano、Emacs 和一个名为 WordStar 的非常古老的文字处理器的键绑定(请注意,对于 CentOS,这个编辑器在标准存储库中不可用,但在第三方存储库中可用)。

注意

如果您想了解 vi,请执行vimtutor命令,这是随 vim 一起提供的教程。这是学习 vi 中所有基础知识、命令和文本编辑的良好起点。

Emacs 带有一个非常好的帮助功能,您可以通过Ctrl + H + R在 Emacs 中访问。

编辑文本流和文件的另一种方法是使用非交互式文本编辑器 sed。它不是通过在文本编辑器窗口中打开文件来编辑文本文件,而是通过 shell 处理文件或流。如果您想要执行以下操作,它是一个方便的实用程序:

  • 自动对文件进行编辑

  • 在多个文件上进行相同的编辑

  • 编写一个转换程序,例如,在小写和大写之间进行转换,甚至更复杂的转换

sed 编辑器的语法与 vi 编辑器的命令非常相似,并且可以进行脚本化。

sed 的默认行为不是编辑文件本身,而是将更改转储到标准输出。您可以将此输出重定向到另一个文件,或者使用-i参数,该参数代表sed命令:

sed -i 's/string/newstring/g' filename.txt

它将搜索一个字符串,替换它,并继续搜索和替换直到文件末尾。

通过一点脚本编写,您可以以相同的方式编辑多个文件:

for files in *conf; do sed -i 's/string/newstring/g' $files; done

您可以将搜索限制为单行:

sed -i '10 s/string/newstring/g' <filename>

sedinfo页面是所有命令的重要资源,更重要的是,它有一个示例部分,如果您想了解更多。

在文件系统中找到自己的方法

现在您知道如何操作和编辑文本文件了,是时候看看这些文件是如何存储在系统中的了。作为系统管理员,您将不得不检查、挂载甚至卸载驱动器。因此,现在让我们仔细看看 Linux 中的文件系统。Linux 文件系统的布局与 Unix 家族的其他成员一样:与 Windows 非常不同。没有驱动器字母的概念。相反,有一个根文件系统(/),并且根文件系统上包括其他已挂载的文件系统在内的所有其他内容都可用。

在本节中,您将了解文件的存放位置以及它们为何在那里。

文件系统层次结构标准

2001 年,Linux 基金会启动了 Linux 标准基础项目(LSB)。基于 POSIX 规范,这个过程的想法是建立一个标准化的系统,使应用程序可以在任何兼容的 Linux 发行版上运行。

文件系统层次结构标准(FHS)是该项目的一部分,定义了目录结构和目录内容。当然,不同发行版之间仍然存在一些关于目录结构的细微差异,但即使在不愿意完全支持 LSB 的发行版上,如 Debian,目录结构也遵循 FHS。

以下截图来自一个 CentOS 系统,使用 tree 实用程序显示目录结构。如果您的系统上没有安装 tree,则 shell 会提示您安装该命令。请安装。

在根文件系统中,有以下目录:

使用 tree 实用程序查看根文件系统的目录结构。

图 3.19:使用 tree 实用程序显示目录结构

tree 命令将以树状结构布局文件系统。或者,您可以使用 ls -lah /以列表格式查看结构。

以下目录在截图中出现:

  • /bin:包含在最小系统上需要由非特权用户执行的程序,如 shell。在基于 Red Hat 的系统中,此目录是指向/usr/bin 的符号链接。命令如 ps、ls 和 ping 都存储在这里。

  • /sbin:包含在最小系统上需要由特权用户(root)执行的程序,如文件系统修复工具。在基于 Red Hat Enterprise Linux 的系统中,此目录是指向/usr/sbin 的符号链接。例如 iptables、reboot、fdisk、ifconfig 和 swapon。

  • /dev:设备挂载在一个叫做 devfs 的特殊文件系统上。所有外围设备都在这里,如串行端口、磁盘和 CPU,但不包括网络接口。例如:/dev/null、/dev/tty1。

  • /proc:进程挂载在一个叫做 procfs 的特殊文件系统上。

  • /sys:sysfs 文件系统上的硬件信息。

  • /etc:由所有程序需要的可编辑文本配置文件组成。

  • /lib:驱动程序和不可编辑文本配置文件的库。库文件名要么是 ld,要么是 lib.so.*,例如 libutil-2.27.so 或 libthread_db-1.0.so。

  • /lib64:驱动程序的库,但没有配置文件。

  • /boot:内核和引导加载程序。例如:initrd.img-2.6.32-24-generic、vmlinuz-2.6.32-24-generic。

  • /root:root 用户的用户数据。只有 root 用户有权写入此目录。/root 是 root 用户的主目录,不同于/。

  • /home:非特权用户的用户数据。类似于 Windows 中的 C:\Users\username 文件夹。

  • /media:可移动介质,如 CD-ROM 和 USB 驱动器,都挂载在这里。每个用户至少有只读权限。例如,/media/cdrom 用于 CD-ROM,/media/floppy 用于软盘驱动器,/media/cdrecorder 用于 CD 刻录机。

  • /mnt:包括远程存储在内的不可移动介质。每个用户至少有只读权限。

  • /run:特定用户或进程的文件,例如应该对特定用户可用的 USB 驱动程序,或者守护进程的运行时信息。

  • /opt:不是发行版的一部分的可选软件,如第三方软件。

  • /srv:静态服务器数据。可用于静态网站、文件服务器和 Salt 或 Puppet 等编排软件。

  • /var:动态数据。从打印队列和日志到动态网站都有。

  • /tmp: 临时文件,在重新启动期间不会保留。现在,它通常是挂载在这个目录上的 RAM 文件系统(tmpfs)。这个目录本身已经过时,从应用程序的角度来看,已经被/var/run中的目录取代。

  • /usr: 包含所有额外的与软件相关的二进制文件、文档和源代码。

再次使用tree命令显示/usr中的目录结构:

/usr 目录中的目录结构

图 3.20:/usr 目录中的目录结构

/usr的目录结构与/的结构非常相似。添加了一些额外的目录:

  • /usr/etc: 如果重新编译已经是发行版的一部分的软件,配置文件应该在/usr/etc中,这样它们就不会与/etc中的文件冲突。

  • /usr/games: 旧游戏的数据,比如fortunefigletcowsay

  • /usr/include: 开发头文件。

  • /usr/libexec: 包装脚本。比如说你需要多个版本的 Java。它们都需要不同的库、环境变量等。包装脚本用于调用具有正确设置的特定版本。

  • /usr/share: 程序数据,如壁纸、菜单项、图标和文档。

  • /usr/src: Linux 内核源代码和发行版中包含的软件的源代码。

  • /usr/local: 你自己安装和编译的软件。

/usr/local的目录结构与/usr相同:

/usr/local 目录中的目录结构

图 3.21:/usr/local 目录的目录结构

这个目录是为了软件开发而存在的。在生产环境中不需要有这个目录。

可选软件放在/opt中。主目录结构是/opt/<vendor>/<software>/,例如/opt/google/chrome。可能的供应商/提供者名称列表由/usr/usr/local维护,有一个例外:你可以在软件目录或/etc/opt目录中选择/conf/etc之间。非本地 Linux 软件,如 PowerShell,可以在软件目录内使用自己的结构。

挂载文件系统

更精确地定义根文件系统可能是个好主意。根文件系统是根目录/所在的文件系统。所有其他文件系统都挂载在这个根文件系统上创建的目录上。要找出哪些目录是根文件系统本地的,哪些是挂载点,执行findmnt命令:

使用 findmnt 命令确定哪些目录是根文件系统本地的,哪些是挂载点。

图 3.22:使用 findmnt 命令查找挂载点

添加-D参数将显示文件系统的大小和可用空间量:

通过运行 findmnt -D 命令列出文件大小和可用空间。

图 3.23:使用 findmnt -D 命令列出文件大小和可用空间

findmnt命令是查找设备挂载位置的好方法,例如:

findmnt /dev/sda1

如果一个目录不是挂载点,使用-T参数:

findmnt -T /usr

第五章,高级 Linux 管理中,详细介绍了不同的文件系统,以及如何挂载和自动挂载本地和远程文件系统。

在文件系统上查找文件

在文件系统上搜索文件可以使用find命令。不幸的是,如果你对这个命令不熟悉,man 页面可能会让人不知所措,而且很难阅读。然而,如果你了解这个命令的基本原理,man 页面将帮助你添加参数来搜索文件或目录的每个属性,或者两者兼而有之。

find命令的第一个可能参数是选项。这些选项影响find命令的行为,即它是否应该遵循符号链接以及调试和速度优化选项。选项是可选的——大多数情况下您不需要它们。

在选项之后,下一个参数告诉find命令在哪里开始搜索过程。从根目录(/)开始搜索不是一个很好的主意;它会花费太多时间,并且可能在大型文件系统上消耗太多 CPU 活动。记住 FHS——例如,如果要搜索配置文件,请从/etc目录开始搜索:

find /etc

上述命令将显示/etc中的所有文件。

在位置之后,下一个参数是包含一个或多个测试的表达式。要列出最常见的测试,请使用以下命令:

  • -typef表示文件,d表示目录,b表示块设备

  • -name <pattern>

  • -user-group

  • -perm

  • -size

  • -exec

您可以执行这些测试的组合。例如,要搜索以conf结尾的文件,请使用以下命令:

find /etc -type f -name '*conf' 

对于一些测试,如sizeatime,可以添加所谓的与提供的参数进行比较:

  • +n:大于n

  • -n:小于n

  • n:正好n

find命令搜索文件和目录,并将它们与n的值进行比较:

find / -type d -size +100M

此示例将搜索内容超过 100MB 的目录。

最后一个参数是应在找到的文件上执行的操作。示例包括:

  • -ls,输出类似于ls命令。

  • -print打印文件名。

  • -printf格式化-print命令的输出。

  • -fprintf将格式化输出写入文件。

-printf参数非常有用。例如,此命令将搜索文件并列出其大小(以字节为单位)和文件名。之后,您可以使用sort命令按大小对文件进行排序:

find /etc -name '*conf' -printf '%s,%p\n' | sort -rn 

还有一些更危险的操作,例如-delete删除找到的文件和-exec执行外部命令。在使用这些参数之前,请非常确定搜索操作的结果。大多数情况下,从性能的角度来看,您最好使用xargs实用程序。此实用程序将结果转换为命令的参数。这样的命令示例如下;grep实用程序用于搜索结果的内容:

find /etc/ -name '*' -type f| xargs grep "127.0.0.1"

进程管理

在前一节中,我们讨论了 Linux 中的文件系统。从系统管理员的角度来看,管理进程至关重要。会有一些情况,您需要启动、停止,甚至杀死进程。此外,为了避免使您的机器过载,您需要谨慎处理系统上运行的进程。让我们更仔细地看看 Linux 中的进程管理。

进程由 Linux 内核运行,由用户启动,或由其他进程创建。所有进程都是进程编号为 1 的子进程,这将在下一章中介绍。在本节中,我们将学习如何识别进程以及如何向进程发送信号。

查看进程

如果您启动一个程序,会有一个/proc

在 Bash 中,您可以使用以下命令找到当前 shell 的 PID:

echo $$

您还可以找到父 shell 的 PID:

echo $PPID

要在文件系统上找到程序的 PID,请使用pidof实用程序:

pidof sshd

您可能会看到 shell 返回多个 PID。如果您只想返回一个 PID,请使用-s参数,表示单次射击:

pidof -s sshd

让我们来看看当前 shell 的proc目录:

导航到/proc 目录并使用 ls 列出其所有内容

图 3.24:当前 shell 的 proc 目录

您可以查看此进程的所有属性。让我们看看其中一些:

  • cmdline:执行此进程的命令

  • environ:此进程可用的环境变量

  • status:文件的状态,UID用户标识符)和拥有该进程的用户/组的GID组标识符

如果执行cat environ,输出将很难阅读,因为换行符是\0而不是\n。您可以使用tr命令将\0转换为\n来解决这个问题:

cat /proc/$$/environ | tr "\0" "\n"

proc目录对故障排除非常有趣,但也有许多工具使用这些信息生成更人性化的输出。其中一个实用程序是ps命令。这个命令有一些奇怪之处;它支持三种不同类型的参数:

  • ps -efps -e -f相同。

  • ps axps a x相同。

  • GNU 风格:由双破折号和长命名选项前导。命令不能分组。

三种样式的输出格式不同,但您可以使用选项修改行为。以下是比较:

使用 ps 命令及其三种不同类型参数运行 ps 命令。

图 3.25:使用带参数的 ps 实用程序

方括号中的进程是内核进程。

您可以查询特定值,例如:

ps -q $$ -o comm

这与以下内容相同:

cat /proc/$$/cmdline

另一个可以帮助您搜索进程的实用程序是pgrep。它可以根据名称和用户等值进行搜索,并默认显示 PID。输出可以使用参数进行格式化,例如使用-l列出进程名称,或使用-o将完整命令添加到输出中。

使用top命令监视进程的交互方式:

使用 top 命令监视进程

图 3.26:使用 top 命令监视进程

top中可见的进程的列中的值与ps中的值相同。在top的手册页中,您可以找到对它们含义的很好解释。其中一些将在后面的章节中介绍。

top命令或更高级的htop命令可以帮助您快速识别占用过多内存或 CPU 的进程,并向进程发送信号。如果您想要详细和高级的进程监视和故障排除,最好使用 Azure 中提供的工具。这将在第十一章,故障排除和监视工作负载中介绍。

向进程发送信号

在现实世界中,您可能会遇到一个问题,即某个特定进程正在消耗大量内存。此时,您可能希望向该进程发送终止信号。同样,在处理进程时,您可能会遇到不同的情况。在本节中,我们将探讨可以发送给进程的不同信号。在信号的手册页第七部分中,您可以找到有关信号的更多信息。信号是发送给进程的消息,例如,改变优先级或终止进程。在本手册中描述了许多不同的信号,但只有少数几个真正重要:

  • 信号 1:这会挂起进程;它将重新加载附加到进程的所有内容。通常用于重新读取更改的配置文件。

  • 信号 2:与Ctrl + CCtrl + Break相同。

  • 信号 3:正常退出进程;与Ctrl + D相同。

  • 信号 15:默认信号,用于终止命令,使终端有时间清理一切。

  • 信号 9:终止命令而不清理。这很危险,可能会使您的系统不稳定,有时甚至会有漏洞。

如果您想要查看可以发送给进程的信号列表,请运行:

kill -l

要向进程发送信号,可以使用top(快捷键k)或kill命令:

kill -HUP <PID>

有一个很好的实用程序可以用来 grep 一个进程或一组进程;它可以一次发送一个信号:pkill。它类似于pgrep。可以根据nameuid等值进行选择。

自主访问控制

现在我们已经介绍了文件系统和进程管理,应该有一种方法来限制您创建的文件的权限。换句话说,您不应该授予每个人对所有内容的访问权限,大多数组织都遵循给予最细粒度权限的原则。自主访问控制DAC)是一种安全实现,它限制对文件和目录等对象的访问。用户或一组用户根据所有权和对象上的权限获得访问权限。

在云环境中,用户和组管理可能不是您日常工作的一部分。通常委托给诸如活动目录AD)之类的身份管理系统,并且您不需要许多用户帐户;现在在应用程序级别进行身份验证和授权更加重要。但是,能够验证用户并了解基础系统的工作原理仍然是一个好主意。

用户管理

如果您在 Azure 中部署虚拟机,在向导中您将指定一个用户,该用户将由 Azure 代理用户管理在虚拟机中创建 - 例如,如果您使用 PowerShell 部署虚拟机:

使用 PowerShell 部署虚拟机

图 3.27:使用 PowerShell 部署虚拟机

您可以使用此帐户登录。这是一个普通用户,也称为非特权用户,没有管理权限。要获得管理权限,您需要sudo命令;sudo表示超级用户执行(或以超级用户身份执行)。没有参数时,su命令将当前用户切换到另一个用户,即 root - Linux 中的管理员帐户。

备注

如果您想要 root 权限,在 Azure 中的某些 Linux 映像中,您不能使用su命令。它默认禁用。要获取 root shell,您可以使用sudo -s。默认情况下,sudo命令会要求您输入密码。

要获取有关此用户帐户的更多信息,请使用getent命令从存储用户信息的passwd数据库中获取实体。这个passwd数据库可以是本地的,存储在/etc/passwd文件中,也可以是远程的,远程服务器将通过检查用户数据库(例如轻量级目录访问协议LDAP))来授予授权:

sudo getent passwd <username>

要获取linvirt用户的详细信息:

使用 getent 获取 linvirt 的详细信息

图 3.28:使用 getent 获取 linvirt 的详细信息

此命令的输出是一个以冒号分隔的列表:

  • 用户帐户名

  • 密码

  • 用户 ID

  • 组 ID

  • 通用电气综合操作系统GECOS)字段用于额外的帐户信息

  • 此用户的主目录

  • 默认 shell

在 Unix 操作系统家族的早期,密码存储在/etc/passwd文件中,但出于安全原因,哈希密码被移动到/etc/shadow。密码可以使用以下命令更改:

sudo passwd <username>

如果要更改当前用户的密码,不需要使用sudo,也不需要指定用户名。您可以使用getent/etc/shadow文件中查看条目:

使用 getent 命令检查密码条目

图 3.29:使用 getent 命令检查密码条目

哈希密码后的列包含可以使用chage命令查看(和更改)的老化信息。阴影数据库中的标记是自 Unix 的虚拟生日(1970 年 1 月 1 日)以来的天数。chage命令将其转换为更易读的形式:

使用 chage 命令将 epoch 转换为可读日期。

图 3.30:使用 chage 命令获取老化信息

让我们回到passwd数据库。用户 ID 的编号在/etc/login.defs文件中定义。ID 0保留给 root 帐户。ID 1200保留给在现代 Linux 系统中不再使用的admin帐户。在基于 Red Hat 的发行版中,范围 201-999 保留给系统帐户,和在这些帐户下运行的守护程序。非特权帐户的范围是 1,000 到 60,000 用于本地用户,>60,000 用于远程用户(例如,AD 或 LDAP 用户)。Linux 发行版之间存在一些小差异。让我们总结一下这些值:

显示 Linux 中用户 ID 号码和用户类型之间关系的表格。

图 3.31:用户 ID 及其保留用户类型

许多发行版都配置了所谓的/etc/login.defs文件:

USERGROUPS_ENAB yes 

这意味着如果您创建一个用户,将自动创建一个与登录名相同的主要组。如果禁用此功能,新创建的用户将自动成为另一个组的成员,该组在/etc/default/useradd中定义:

GROUP=100

可以使用chfn命令更改 GECOS 字段:

使用 chfn 命令更改 GECOS 字段

图 3.32:使用 chfn 命令更改 GECOS 字段

注意:

chfn(更改 finger)命令是指一个旧实用程序finger,它不是默认安装的,但仍然可以在存储库中找到。还有一个finger守护程序,可以通过网络提供 GECOS 信息,但被认为是安全风险。

在创建用户时,默认 shell 在/etc/default/useradd中定义。您可以使用chsh命令将默认 shell 更改为另一个。shell 必须在/etc/shells文件中列出:

chsh -s /bin/tcsh linvirt

为了本书的目的,保持 Bash 作为默认 shell。

在本节中,您学习了如何验证和更改现有本地用户的属性。当然,您也可以添加额外的用户:

sudo useradd <username>

useradd命令有很多自定义选项。您可以使用man useradd了解更多信息。或者,您可以使用adduser命令:

使用 adduser 命令添加用户

图 3.33:使用 adduser 命令添加用户

组管理

如前一章所述,用户将成为主要组的一部分。当您创建用户时,如果不指定组,将自动创建一个与用户名相同的组。如果您检查前面的屏幕截图,您可以看到一个名为john的组,用于用户john

除了是主要组的成员之外,还可以添加额外的组成员资格。这是为了访问组目录/共享或在sudo配置中委派权限而必要的。您可以在创建用户时使用useradd命令的--groups参数添加现有的额外组成员,或者之后使用usermodgroupmems

让我们创建一个新用户和一个新组,并验证结果:

sudo useradd student
sudo passwd student
sudo getent passwd student
sudo groupadd staff 
sudo getent group staff

student用户添加到staff组:

sudo groupmems -g staff -a student

或者:

sudo usermod –aG staff student
sudo groupmems -g staff -l
sudo getent group staff

您可以使用switch groupsg)临时更改您的主要组:

su student
id -g 
sg staff

注意:

虽然不太常见,但您可以使用gpasswd命令为组帐户添加密码。这样,不属于该组的用户仍然可以使用sg并输入该组的密码。

一个非常特殊的组是wheel组。在sudo配置中,属于这个组的用户能够执行需要管理员权限的命令。在 Ubuntu 中,这个组不可用;而是有一个名为sudo的组,可以用于相同的目的。

登录管理

在企业环境中,管理员需要收集诸如登录用户数、无效登录数以及任何授权用户尝试登录的信息,以进行安全审计。在本章中,我们将介绍 Linux 中的登录管理,这在安全方面至关重要。

任何对 Linux 系统的登录都会被一个名为systemd-logind的服务跟踪和管理,以及一个相应的命令:loginctl。这个命令适用于所有的 Linux 发行版;然而,如果你使用Windows 子系统用于 LinuxWSL),由于缺乏 systemd,这将不可用。

这个命令的参数分为用户、会话和座位三个部分。要使用这些参数进行一些练习,使用学生账户的凭据在你的 VM 上打开第二个ssh会话。在第一个ssh会话中执行命令。

首先,列出会话:

loginctl list-sessions

记录会话 ID 和特定会话的详细信息:

loginctl show-session <session number>

在我的情况下,会话 ID 是27,所以我们将使用loginctl来检查会话详细信息:

使用 loginctl 检查会话 ID 27 的会话详细信息

图 3.34:检查会话 ID 27 的会话详细信息

查看用户属性:

loginctl show-user <username>

切换到第二个 SSH 会话并执行man man

现在切换登录管理回到第一个 SSH 会话,并使用user-status参数查看学生的状态:

使用用户状态参数查看学生的状态

图 3.35:使用用户状态参数

最后,终止会话:

sudo loginctl terminate-session <session id>

还有一个terminate-user参数,如果一个会话中有多个用户,这可能会很方便。

总结

本章是一个关于如何在 Linux 中生存的速成课程,如果你对这个操作系统不熟悉的话。这一章并不是关于如何成为一名高级 Linux 管理员。

在你作为 Azure 管理员的日常生活中,你可能不会使用本章中的所有内容。例如,你可能不会在虚拟机中创建用户。但是你应该能够验证在诸如 AD 之类的身份管理系统中配置的用户,并验证他们能够登录。

本章主要讲述了如何使用 shell、文件系统的结构以及查找文件。我们看了一下文本文件在 Linux 中的作用以及如何处理和编辑它们。我们处理了进程,并学会了如何查看和终止它们。最后但并非最不重要的是,我们看了用户和组管理。

在下一章中,我们将讨论在 Azure 中管理资源。

问题

在本章中,我不想回答一些问题,而是让你做一个练习:

  1. 创建用户LisaJohnKarelCarola

  2. 为这些用户设置密码为welc0meITG

  3. 验证这些用户的存在。

  4. 创建financestaff组。

  5. 使用户LisaCarola成为finance的成员,KarelJohn成为staff的成员。

  6. 创建/home/staff 和/home/finance 目录,并将这些目录的组所有权分别设置为 staff 和 home。

  7. 给予 staff 组对 finance 目录的读取权限。

  8. 确保新创建的文件获得正确的组所有权和权限。

进一步阅读

有很多为 Linux 操作系统新用户出版的书籍。以下是我个人喜欢的一些。

与 Linux 一起工作-命令行的快速技巧(ISBN 978-1787129184)由 Petru Işfan 和 Bogdan Vaida 是一个奇怪的收集,里面有很多不错的技巧,有时这就是你所需要的。

如果你能阅读德语,那么 Michael Kofler(kofler.info)的所有书籍都应该放在你的书架上,即使你是一名经验丰富的 Linux 用户!

微软网站上有关于正则表达式的非常好的文档:docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expressions。如果你想练习使用正则表达式,我也喜欢regexone.com

awk实用程序附带有一本大型手册(www.gnu.org/software/gawk/manual/gawk.html),但也许不是最好的起点。Shiwang Kalkhanda 在《学习 AWK 编程》(ISBN 978-1788391030)中做得非常好,编写了一本非常易读的书。不要害怕这个标题中的“编程”一词,特别是如果你不是开发人员;你应该阅读这本书。

第四章:管理 Azure

第二章开始使用 Azure 云中,我们迈出了进入 Azure 世界的第一步。我们发现有许多方法可以管理 Azure 环境,其中包括 Azure 门户和命令行接口。你可以在 Azure 门户中使用命令行接口在你的工作站上运行它们。在本书的后面,我们将看到使用自动化和编排解决方案还有其他很好的可能性。在第三章基本 Linux 管理结束时,我们创建了一个 Linux 虚拟机,并探索了 Linux 环境的基础知识。

在我们继续探讨更高级的主题之前,本章涵盖了 Azure 基础架构的组件,这些组件是我们工作负载、虚拟机和容器所需的。我们已经讨论了 Linux 中的文件系统,但是我们如何向虚拟机添加更多的数据磁盘呢?作为系统管理员,你可能需要根据业务需求允许或拒绝流量到你的虚拟机,但这在 Azure 中如何实现?在某些情况下,你需要将多个网络接口连接到虚拟机。你将如何完成这个任务?本节将回答你关于如何管理与虚拟机相关的 Azure 资源的所有问题。我们谈论的是我们在以前的章节中已经使用过的组件,有时甚至是在不知情的情况下。

基本上,本章是关于 Azure 资源的。记住,它们都是资源组的一部分,资源组是一个逻辑容器,用于部署和管理资源。以下是本章的一些关键要点:

  • 管理 Azure 中的存储资源和不同的存储选项

  • 管理 Azure 中的网络资源并了解它们在虚拟机中的作用

  • 使用 PowerShell 和 Azure CLI 中的便捷命令来管理 Azure 中的资源

注意

在这本书中,我们尽可能中立地对待可用的接口。因为这一章更多关于理论而不是接口,我们将以 PowerShell 作为示例。

使用 Azure CLI 和 PowerShell 管理 Azure 资源

在本章中,我们将看到如何使用 PowerShell 和 Azure CLI 来管理 Azure 资源。我们在这里要做的每一个任务也可以从 Azure 门户中完成。然而,作为一个每天都在终端执行任务的系统管理员,你应该能够使用 CLI 或 PowerShell 来管理你的资源。本章中几乎每个命令都是用 PowerShell 编写的;然而,在本章结束时,你会找到每个 PowerShell 命令的 Azure CLI 等效命令。命令列表太长了,最好参考官方的微软文档或使用相应的帮助命令。

在某些情况下,甚至在接下来的章节中,我们将使用 Azure 门户。这是为了简化流程,并让你们都了解另一种方法。如果你愿意,你可以使用门户,但是当涉及自动化任务和编排部署时,CLI 或 PowerShell 体验是必不可少的。因此,我们鼓励你们在本章中遵循 PowerShell 命令,并投入时间测试使用 Azure CLI 的等效命令。

以下是本章完成任务的一些技术要求。

技术要求

对于本章,需要有存储和网络的基本知识。在进一步阅读部分,你可以找到一些准备自己的建议。

虽然不是必需的,但最好至少有一个虚拟机正在运行。这样,你不仅可以在本章中创建新资源,还可以查看现有虚拟机的属性。在本节中,我们将在chapter4资源组中创建一个名为ubuntu01的 Ubuntu 虚拟机作为示例。

设置资源组和位置的变量:

$myRG = "chapter4"
$myLocation = "westus"
$myTestVM = "ubuntu01"

创建资源组:

New-AzResourceGroup -Name $myRG -Location $myLocation

创建虚拟机:

  New-AzVm '
  -ResourceGroupName $myRG '
  -Name $myTestVM '
  -ImageName UbuntuLTS '
  -Location $myLocation '
  -VirtualNetworkName "$myTestVM-Vnet" '
  -SubnetName $myTestVM-Subnet '
  -SecurityGroupName "$myTestVM-NSG" '
  -PublicIpAddressName $myTestVM-pip

目前,在此示例中使用的参数并不重要;在本章结束时,您将能够理解它们。

不是真正的要求,但很好拥有的是 Azure Storage Explorer 实用程序,可以免费在azure.microsoft.com/en-us/features/storage-explorer上获得。这是一个独立的实用程序,可安装在您的工作站上。此实用程序将帮助您上传、下载和管理 Azure blob、文件、队列和表。它还支持 Azure Cosmos DB 和 Azure Data Lake。另一个优势是您可以访问连接到虚拟机的磁盘。Storage Explorer 也作为 Azure 门户中的选项可用。

管理存储资源

微软的云解决方案用于处理数据存储的是 Azure 存储。Azure 存储提供高可用性、安全性、可扩展性和可访问性。在 Azure 中,我们有不同类型的数据或存储服务。它们是:

  • Azure Blob

  • Azure 文件

  • Azure 队列

  • Azure 表

让我们更仔细地看看每一个,并了解它们是什么:

  • Azure Blob:用于存储大量非结构化数据的优化对象,例如文本或二进制数据。它们通常用于使数据可用于其他资源,例如存储可用于创建虚拟磁盘的 VHD 文件。另一个用例是将它们用作音频和视频文件的存储。通过使 blob 公开访问,甚至可以流式传输数据。

  • Azure 文件:Azure 文件是托管在 Azure 中可以通过服务器消息块SMB)访问并可以挂载到本地计算机的文件共享。您可能想知道这些与普通文件共享有何不同。这里的附加优势是生成的 URL 将包括一个共享访问签名SAS),并且文件共享将能够从世界各地访问。

  • Azure 队列:用于将消息从一个资源传递到另一个资源,特别是用于无服务器服务,如 Azure Web 应用程序和函数。它还可以用于创建异步处理的工作积压。

  • Azure 表:用于 Azure Cosmos DB 服务。

  • Azure 磁盘:这是用于托管磁盘存储和非托管磁盘的。

在本章中,我们只会涵盖 Blob 存储、Azure 文件和磁盘存储,因为队列和表存储是针对特定解决方案的,只对应用程序开发人员重要。

注意

如果您有大量数据要存储在云中,上传可能需要太长时间。微软有一个名为 Azure Data Box Disk 的服务。它允许您将加密的 SSD 发送到您的数据中心,复制数据,然后发送回来。有关更多信息,请访问docs.microsoft.com/en-gb/azure/databox/data-box-disk-overview

存储帐户

存储帐户提供了一个在 Azure 中唯一的命名空间,用于包含诸如 blob、文件、表、队列等存储对象。需要帐户来访问存储。它还定义了正在使用的存储类型。

有三种不同类型的存储帐户:

  • 存储:这种旧类型的已弃用存储帐户不支持所有功能(例如,没有存档选项)。它通常比较新的 V2 更昂贵。

  • StorageV2:这是一种更新的存储帐户类型。它支持所有类型的存储和最新的 blob、文件、队列和表功能。

  • BlobStorage:尚未被弃用,但不再有理由使用它。这种帐户类型的最大问题是无法存储 VHD 等文件。

注意

对于托管磁盘,您无需创建存储账户。但是,如果您想要存储 VM 引导诊断数据,您将需要一个。引导诊断账户在 VM 进入非可引导状态时非常有用。存储在此账户中的日志可用于查找 VM 无法引导状态的根本原因。对于测试来说,这不是一个强制性选项,但对于生产工作负载,建议启用引导诊断,这将帮助您了解故障发生时出了什么问题。

另一个属性是 SKU,如第二章,开始使用 Azure 云中所述。它指定了适用于存储账户的复制类型。以下是可用的类型,如果您还记得,我们已经讨论过它们是什么:

  • Standard_LRS:本地冗余存储账户

  • Premium_LRS:与 LRS 相同,但支持 FileStorage 和 BlockBlobStorage

  • Standard_GRS:地理冗余存储账户

  • Standard_RAGRS:具有读取访问地理冗余存储账户

  • Standard_ZRS:区域冗余存储账户

最后一个重要属性是访问层;它指定了存储的优化。有三种类型可用:

  • 热存储层:需要频繁访问的数据将存储在热存储层中。

  • 冷存储层:很少访问的数据,存储期至少为 30 天。

  • 归档存储层:很少访问的数据,存储期至少为 180 天,具有灵活的延迟要求。

设置对象级别的访问层仅支持 Standard LRS、GRS、RA-GRS BlobStorage 和通用 V2 账户。通用 V1GPv1)账户不支持分层。

访问层的选择也会影响成本;例如,归档存储提供了最低的存储成本,但也提供了最高的访问成本。

存储账户名称长度必须在 3 到 24 个字符之间,并且只能使用数字和小写字母。存储账户名称在 Azure 中必须是唯一的。微软建议使用全局唯一名称和一个随机数:

New-AzStorageAccount '
  -ResourceGroupName <resource group> '
  -SkuName <sku> '
  -Location <location> '
  -Kind StorageV2 '
  -AccessTier <access tier> '
  -name <storage account>

让我们创建一个冗余为 Standard_LRS 的存储账户:

$mySA = New-AzStorageAccount '
  -ResourceGroupName $myRG '
  -SkuName Standard_LRS '
  -Location $myLocation '
  -Kind StorageV2 '
  -AccessTier Hot '
  -name chapter4$(Get-Random -Minimum 1001 -Maximum 9999)

检查您的订阅中可用的存储账户:

Get-AzStorageAccount | Select StorageAccountName, Location

检查可用的存储账户、名称和位置。

图 4.1:可用存储账户

在屏幕截图中,您可以看到此订阅中有三个不同区域的存储账户可用。

存储账户受密钥保护。如果您想要访问存储账户,您将需要密钥。在创建账户时会自动创建一组两个密钥。如果您仍然处于创建账户时的同一会话中,您可以接收密钥:

$mySA | Get-AzStorageAccountKey | Format-Table -Wrap

否则,您可以使用以下内容:

Get-AzStorageAccountKey '
  -ResourceGroupName <resource group>'
  -Name <storage account name>

在下面的屏幕截图中,位于$MyRG 资源组中的chapter42298存储账户有一组受保护的密钥:

获取位于$MyRG 资源组中的 chapter 42298 存储账户的受保护密钥。

图 4.2:获取 chapter42298 存储账户的密钥

托管磁盘

早些时候,当我们部署 VM 时,我们需要创建一个存储账户,用于保存 VM 的虚拟硬盘VHD)。后来,微软推出了托管磁盘,我们可以简单地创建一个磁盘,微软负责底层存储账户。此外,客户还可以获得额外的优势,如轻松调整大小、更多的加密选项和更好的性能。

当您使用托管磁盘创建 VM 时,将会附加两个磁盘到 VM:一个操作系统磁盘和一个临时磁盘。所有磁盘都以 VHD 格式存储。当您重新启动 VM 时,临时磁盘上存储的数据将被清除,因此微软不建议在临时磁盘上存储重要数据,因为它不是持久的。

您还可以添加额外的托管数据磁盘。首先,创建磁盘配置:

New-AzDiskConfig -Location <location>'
  -DiskSizeGB <size> -OsType Linux -SkuName <sku>  '
  -CreateOption empty

让我们看看如何创建一个大小为 5GB 且冗余为 Standard_LRS 的示例磁盘配置:

$diskconfig = New-AzDiskConfig -Location $myLocation '
  -DiskSizeGB 5 -OsType Linux -SkuName Standard_LRS '
  -CreateOption empty

现在,您可以创建实际的磁盘:

New-AzDisk -ResourceGroupName <resource group name> '
  -DiskName <disk name> -Disk <disk configuration>

例如,以下是前面命令的实现:

$Disk01 = New-AzDisk -ResourceGroupName $myRG '
  -DiskName 'Disk01' -Disk $diskconfig

通过执行$Disk01命令,您将看到新创建的磁盘。在以下屏幕截图中,输出被限制以使其更易读:

使用$Disk01 命令创建新磁盘。

图 4.3:$Disk01 命令的输出

下一步是附加托管数据磁盘。为此,我们需要磁盘 ID。因此,我们将使用磁盘名称运行以下命令以查找 ID:

Get-AzDisk -DiskName <disk name> | select Id

添加数据磁盘:

Add-AzVMDataDisk -VM $myVM -Name <disk name> '
  -ManagedDiskId <disk id> -Lun <lun number> -CreateOption Attach 

逻辑单元号LUN)是用于标识 VM 中存储的数字。您可以从零开始编号。最后,更新 VM 设置:

Update-AzVM '
  -ResourceGroupName <resource group> '
  -VM <virtual machine>

现在,您可以将数据磁盘添加到 VM。总结一个完整的示例,首先您需要 VM 的所有属性。要获取 VM 的属性,我们将使用以下命令并将属性保存到变量$myVM中:

$myVM = Get-AzVM -ResourceGroupName $myRG -Name $myTestVM 

下一个命令是将之前创建的磁盘添加到 VM 中:

Add-AzVMDataDisk -VM $myVM -Name Disk01 '
  -ManagedDiskId $Disk01.Id -Lun 1 -CreateOption Attach

上述命令将显示 VM 的配置属性,如此屏幕截图所示:

通过在 VM 上添加创建的磁盘来配置 VM 的属性。

图 4.4:在 VM 上添加创建的磁盘

从输出中可以看出,信息已添加到StorageProfile,但更改尚未生效。

要使其生效,请使用Update-AzVM。输出应该给出StatusCodeOK

Update-AzVM -ResourceGroupName $myRG -VM $myVM

如您在以下屏幕截图中所见,IsSuccessStatusCode告诉您请求已收到。StatusCode是请求的结果:

要使 StorageProfile 生效,请使用 Update-AzVM 命令更新 StatusCode。

图 4.5:使用 Update-AzVM 命令更新 StatusCode

验证结果:

$myVM.StorageProfile.DataDisks

或者,更好的是,不要重用变量,只需在这个一行命令中查询所有信息:

$(Get-AzVM -Name $myTestVM '
  -ResourceGroupName $myRG).StorageProfile.DataDisks

您可以看到名称、大小和 LUN:

表示磁盘 StorageProfile 的名称、大小和 LUN 的输出。

图 4.6:磁盘存储配置

Azure Files

您可以使用Azure Files而不是向 VM 添加数据磁盘。如果您还记得,我们在本章的开头讨论了 Azure Files,并提到它与普通文件共享不同。Azure Files 是云中完全托管的文件共享,可以通过服务器消息块SMB)访问,并且可以挂载到 Linux、Windows 和 macOS。

Azure Files 需要一个存储账户,并支持 Standard_LRS、Standard_ZRS、Standard_GRS 和 Standard_ZRS(仅在选定的区域)SKU 类型。除了标准(热)可用之外,没有高级存储或其他访问层。 (在撰写本书时,微软的消息来源表示目前没有可用于引入这些功能的时间表。)

请注意,出于性能原因,您确实需要 SMB 3.0 协议。这意味着您需要一个最新的 Linux 发行版,比如这里列出的一个:

  • 基于 RHEL 的发行版 7.5 或更高版本

  • Ubuntu 16.04 或更高版本

  • Debian 9

  • SUSE SLE 12 SP3 / OpenSUSE LEAP 42.3 或更高版本

您还需要使用挂载选项强制版本 3:vers=3.0

第一步涉及创建 Azure Files 共享:

New-AzStorageShare '
  -Name <share name> -Context <storage account context> 

对于存储账户上下文,您可以使用用于创建存储账户的变量或重新创建变量:

$mySA = (Get-AzStorageAccount | Where-Object {$_.StorageAccountName -Like "chapter*"})

让我们实现这个并创建一个新的文件共享:

$myShare01 = New-AzStorageShare '
  -Name "myshare01-staff" -Context $mySA.Context

让我们检查一下$myShare01的值。输出清楚地显示了存储的 URL、创建时间以及快照是否可用:

显示 myShare01 值的存储 URL。

图 4.7:myShare01 的输出

要查看创建的共享的属性,请执行以下命令:

(Get-AzStorageShare -Context $mySA.Context).Uri

如下面的屏幕截图所示,它将为您提供相同的输出,但会提供一些更多的信息,这对我们的目的并不重要:

创建的共享属性的更多详细信息

图 4.8:创建的共享属性

在 Linux 中,您可以使用以下代码手动挂载文件共享:

mount -t cifs \
  -o vers=3.0,username=<storage account>,password=<storage key>\
  //<storage account name>.file.core.windows.net/<share> \
  /<mount point>

请注意,我们不使用 HTTPS 方案,因为 CIFS 不使用 URI。Azure 将负责在不同方案之间进行映射。

让我们继续挂载文件共享。您的密码和存储文件共享将与示例中的不同,因为名称在 Azure 中是唯一的:

mkdir /mnt/staff

mount -t cifs -o vers=3.0,username=chapter42585,password=.... \
  //chapter42585.file.core.windows.net/myshare-01.staff /mnt/staff

此外,您还可以在 Azure 门户(portal.azure.com/)中使用连接选项进行文件共享,Azure 将生成挂载共享到 Linux 以及 Windows 和 macOS 系统的命令。

在下面的屏幕截图中,您可以看到单击“连接”时,Azure 会生成代码,将文件共享连接到 Linux 系统。您可以复制此代码并粘贴到您的 Linux 系统中:

单击“连接”选项卡时,Azure 会生成代码,将文件共享连接到 Linux 系统

图 4.9:将文件共享连接到 Linux 系统

有关 Azure 文件挂载共享的更多信息,请参阅《第五章,高级 Linux 管理》中的《挂载远程文件系统》部分。以下是 Azure 文件的挂载单元的示例:

[Unit]
Description = Staff Share
[Mount] 
What = //chapter42585.file.core.windows.net/myshare-01.staff
Where = /mnt/staff 
Type = cifs
Options = vers=3.0,credentials=/root/.staff

在这里,/root/.staffs文件包含以下条目:

username=<storage account>
password=<key>

验证共享和管理内容的另一种好方法是使用 Azure 存储资源管理器。在您的工作站上启动 Azure 存储资源管理器并连接您的 Azure 账户。如果您不想添加整个账户,您也可以选择使用 SAS 密钥仅添加存储账户。存储资源管理器将在左侧显示不同类型的资源,如此屏幕截图所示:

Azure 存储资源管理器窗口显示不同类型的资源

图 4.10:Azure 存储资源管理器

Azure Blob

Azure Blob 存储是一种存储服务,用于在云中以对象形式存储非结构化数据(图像、视频、音频、备份数据等,不符合数据模型)。Blob 是基于对象的存储,可以存储任何类型的数据。

在存储账户中,您可以拥有容器;容器与计算机上的目录或文件夹非常相似。例如,如果您将喜爱的音乐文件存储在 Azure 中,您可以将账户名称设置为音乐,在其中,您可以根据流派或艺术家创建一个容器,实际的音乐文件就是 blob。一个存储账户可以拥有无限数量的容器,一个容器可以拥有无限数量的 blob。

Azure 文件共享是将数据保存在虚拟机之外的一种好方法。但它们是基于文件的,并不是每种数据类型的最快选择。例如,从 Azure 文件进行流式传输虽然可能,但性能并不是很好;上传非常大的文件也可能非常具有挑战性。Blob 存储是这个问题的解决方案,它的扩展性更好:Azure 文件共享为 5TB,单个 Blob 容器为 500TB。

要能够上传 blob,您必须首先创建一个容器:

New-AzStorageContainer -Name <container name> '
  -Context <context of storage account> -Permission blob

以下是创建容器列表的示例:

$myContainer = New-AzStorageContainer '
  -Name container01 '
  -Context $mySA.context -Permission blob

显示创建容器列表的输出

图 4.11:创建容器列表

创建容器时有三种权限可用:

  • 容器:提供对容器及其 blob 的完全读取访问。客户端可以通过匿名请求枚举容器中的 blob;其他容器是不可见的。

  • Blob:通过匿名请求提供对容器中的 blob 数据的读取访问,但不提供对容器数据的访问。其他 blob 是不可见的。

  • 关闭:限制访问仅限于存储账户所有者。

您可以再次使用 Azure 存储资源管理器查看容器:

使用 Azure 存储资源管理器窗口查看容器。

图 4.12:使用 Azure 存储资源管理器查看容器

使用 PowerShell,您可以创建一个 blob:

Set-AzStorageBlobContent -File <filename> '  
  -Container <container> -Blob <blobname> '  
  -Context $mySA.context

您可以使用以下命令验证结果:

Get-AzStorageBlob -Container <container name> '
  -Context $mySA.context | select Name 

现在,您可以将文件上传到容器,将其转换为 blob,例如:

Set-AzStorageBlobContent -File "/Pictures/image.jpg" '   
  -Container $myContainer.Name ' -Blob "Image1.jpg" '   
  -Context $mySA.context 

您还可以列出结果:

Get-AzStorageBlob -Container <container> '
  -Context $mySA.context | select Name 

所有这些操作也可以在 Bash 中执行。

您可以使用Blobfuse从 Linux Blobfuse 参考链接中挂载 blob;有关更多信息,请访问github.com/Azure/azure-storage-fusedocs.microsoft.com/en-us/azure/storage/blobs/storage-how-to-mount-container-linux

将数据复制到 blob 的另一种解决方案是AzCopy(有关更多信息,请访问docs.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-linux)。

但是,老实说,大多数情况下,这不是您将使用 Blob 存储的方式。Blob 存储不是您希望在操作系统级别访问的内容,而是在应用程序级别访问的内容,用于存储诸如图像之类的对象并使其公开可用。微软提供了一些很好的入门示例,网址为github.com/Azure-Samples?q=storage-blobs

第七章,部署您的虚拟机中,有一个很好的例外示例:上传 VHD 文件以创建使用该 VHD 的自定义映像。

管理网络资源

如前所述,在第三章基本 Linux 管理中,网络非常重要。Azure 虚拟网络是 Azure 提供的一项服务,提供以下功能:

  • 与工作负载的连接

  • 工作负载与外部世界的连接

  • 虚拟机之间的连接

  • 其他连接选项,如 VPN 隧道

  • 流量过滤

  • 包括通过 VPN 隧道的 BGP 路由在内的高级路由选项

虚拟网络

在 Azure 中,虚拟网络的最重要组件是虚拟网络VNet。虚拟网络至关重要,因为它为您的 VM 提供了一个高度安全的隔离环境。

以下过程可能看起来有点混乱和冗长,但这里的目的是让您了解过程和命令。让我们从创建虚拟网络开始:

AzVirtualNetwork -Name <vnet name> '
  -ResourceGroupName <resource group> -Location <location>'
  -AddressPrefix <network>

因此,如果我们想要创建名称为MyVirtualNetwork、地址空间为10.0.0.0/16的虚拟网络,我们将使用:

$myVnet = New-AzVirtualNetwork -Name MyVirtualNetwork '
  -ResourceGroupName $myRG -Location $myLocation '
  -AddressPrefix "10.0.0.0/16"

执行刚刚创建的变量将显示所有属性:

显示虚拟网络属性的输出

图 4.13:虚拟网络属性

AddressSpace或地址是一个或多个子网可以使用的网络。可以添加额外的地址空间。

子网

如前所述,子网是在虚拟网络中创建的。在 Azure 中,同一网络中不同子网之间的所有流量都经过路由,因此子网可以相互访问。当然,您可以修改该行为,例如,当您想要使用负载均衡器时。

同样,出于与虚拟网络相同的原因,我们将使用最简单的命令:

Add-AzVirtualNetworkSubnetConfig '
  -AddressPrefix <subnet> -Name <subnet> '
  -VirtualNetwork <vnet>

要创建名称为MySubnet、地址池为10.0.1.0/24的子网,请执行以下命令:

$mySubnet = Add-AzVirtualNetworkSubnetConfig '
  -AddressPrefix 10.0.1.0/24 -Name MySubnet '
  -VirtualNetwork $myVnet

注意

可能会收到一条警告,指出某些对象已被弃用。您可以放心忽略它。

如果执行$mysubnet,您将看到子网已添加:

显示使用$myVnet.Subnets 的子网详细信息的输出

图 4.14:子网详细信息

如前面的屏幕截图所示,我们没有使用整个网络,只使用了其中的一部分。

或者,可以使用以下命令进行验证:

Get-AzVirtualNetworkSubnetConfig '
  -VirtualNetwork $myVnet -Name MySubnet

输出将与前面的截图完全相同。

子网的第一个 IP 地址是来自虚拟机的网络流量的网关;它提供以下内容:

  • 默认网关,带有源网络地址转换SNAT)以获得互联网访问。为了能够这样做,必须配置公共 IP 地址。SNAT 允许您将来自私有网络中的虚拟机(或任何资源)生成的流量发送到互联网通过网关。

  • DNS 服务器,如果未另行配置。

  • DHCP 服务器。

虚拟网络配置的最后一部分涉及附加新创建的子网:

    Set-AzVirtualNetwork -VirtualNetwork $myVnet

从输出中,除其他信息外,您可以看到地址空间和子网:

通过附加新创建的子网,您可以查看地址空间和子网

图 4.15:附加新创建的子网

网络安全组

网络安全组NSG)是我们需要关注的下一个组件。它本质上是与子网关联的访问控制列表。它还为虚拟机或容器提供端口转发。规则适用于附加到子网的所有接口。

第一步是创建一个 NSG:

New-AzNetworkSecurityGroup '
  -ResourceGroupName <resource group>'
  -Location <location> -Name <nsg name>

例如,您可以通过以下方式创建 NSG:

$myNSG = New-AzNetworkSecurityGroup '
  -ResourceGroupName $myRG -Location $myLocation -Name myNSG1

在庞大的输出中,您将找到几个部分;其中一个部分名为默认安全规则。该部分包含一组规则,按优先级顺序给出:

  • 允许来自虚拟网络中所有虚拟机的入站流量(AllowVnetInBound)

  • 允许来自 Azure 负载均衡器的入站流量(AllowAzureLoadBalancerInBound)

  • 拒绝所有入站流量(DenyAllInBound)

  • 允许来自虚拟网络中所有虚拟机到所有虚拟机的出站流量(AllowVnetOutBound)

  • 允许来自所有虚拟机到互联网的出站流量(AllowInternetOutBound)

  • 拒绝所有出站流量(DenyAllOutBound)

在进入规则之前,让我们将子网与 NSG 关联起来:

Set-AzVirtualNetworkSubnetConfig -Name <subnet name> '
  -VirtualNetwork <vnet> -NetworkSecurityGroupID <nsg id> '
  -AddressPrefix <subnet> 

例如,以下是前述命令的实现:

$NSGSubnet = Set-AzVirtualNetworkSubnetConfig '
  -Name $myVnet.Subnets.Name '
  -VirtualNetwork $myVnet '
  -NetworkSecurityGroupID $myNSG.Id '
  -AddressPrefix 10.0.1.0/24 

您可能会收到与之前看到的相同的弃用警告。您可以再次忽略它们。将 NSG 附加到网络:

$NSGSubnet | Set-AzVirtualNetwork

此命令的输出将为 JSON 格式,并且由于所有参数的原因而很长。如果您查看输出,您将看到NetworkSecurityGroup被称为myNSG1,这是我们创建的 NSG:

将 NetworkSecurityGroup 附加到网络

图 4.16:附加到网络的 NSG

如果我们想要使用 SSH 访问我们的虚拟机,那么我们需要添加一个安全规则:

$myNSG | Add-AzNetworkSecurityRuleConfig -Name SSH '
  -Description "Allow SSH" '
  -Access Allow -Protocol Tcp -Direction Inbound '
  -Priority 100 '
  -SourceAddressPrefix Internet -SourcePortRange * '
  -DestinationAddressPrefix * '
  -DestinationPortRange 22 | Set-AzNetworkSecurityGroup

-SourceAddressPrefix参数是一种缩写,表示虚拟网络之外并可通过公共互联网到达的一切。其他值如下:

  • VirtualNetwork:此虚拟网络及其他连接的网络中的一切。

  • AzureLoadBalancer:如果您正在使用 Azure 负载均衡器,这将为您的虚拟机提供访问。

  • *:一切。

优先级范围从1004096。较高的数字由 Azure 创建并可以被覆盖。优先级数字越低,规则的优先级越高。

前一个命令的输出可能包含太多信息,这有点令人困惑。为了确认端口22的流量是否被允许,我们将使用以下命令过滤输出:

$myNSG | select SecurityRules
$myNSG.SecurityRules

如下截图所示的输出验证了 TCP 端口22对入站流量是开放的。该端口的优先级为100,但由于它是唯一的规则,这并不重要:

输出以验证 TCP 端口 22 是否开放以进行入站流量

图 4.17:列出为 NSG 设置的安全规则

或者,您可以使用以下命令:

$myNSG | Get-AzNetworkSecurityRuleConfig

正如您所看到的,输出是相同的。

公共 IP 地址和网络接口

要能够从互联网访问虚拟机,需要一个公共 IP 地址以及一个 DNS 标签,这是赋予我们的虚拟机的 DNS 名称。

公共 IP 可以是静态的或动态的。对于动态公共 IP,每当您取消分配然后重新启动 VM 时,IP 将被释放并与 VM 解除关联。下次启动 VM 时,将为 VM 分配一个新的公共 IP。因此,每次您取消分配然后重新启动 VM 时,都必须从 CLI 或门户验证公共 IP 以连接到 VM。

DNS 标签的重要部分来了:如果您已经向 VM 添加了 DNS 标签,无论 VM 具有的公共 IP 是什么,您都可以始终使用该标签连接到 VM。当您取消分配并重新启动 VM 时,DNS 标签不会更改。此外,DNS 标签在 Azure 中是唯一的。

对于静态公共 IP,IP 将为您保留。即使您取消分配然后重新启动 VM,IP 也不会更改。将静态 IP 分配给 VM 不会阻止您添加 DNS 标签。如果需要,您也可以添加标签。

使用以下命令创建新的动态公共 IP:

$pip = New-AzPublicIpAddress '
  -ResourceGroupName $myRG '
  -Location $myLocation -AllocationMethod Dynamic '
  -Name "$(Get-Random)" 

通过查看$pip变量的内容来验证。如果分配方法是动态,则在将 IP 地址分配给网络接口之前,不会分配 IP 地址:

通过查看$pip 变量的内容来验证新的动态公共 IP。

图 4.18:验证新的动态公共 IP

因此,在前面的截图中,IpAddress字段显示为未分配

使用以下命令创建网络接口:

$nic = New-AzNetworkInterface -Name myNic '
  -ResourceGroupName $myRG -Location $myLocation '
  -SubnetId $myVnet.Subnets[0].Id -PublicIpAddressId $pip.Id '
  -NetworkSecurityGroupId $myNSG.Id

如果在SubnetId上出现错误,请尝试重新设置myVnet变量并运行以下命令:

$myVnet = Get-AzVirtualNetwork -Name $myVnet.Name '
  -ResourceGroupName $myRG

要验证结果,请执行以下命令:

$nic.ipConfigurations

检查分配给网络接口的 IP 地址

图 4.19:检查分配给网络接口的 IP 地址

在输出中,正如您在前面的截图中所看到的,分配了一个 IP 地址,这次是10.0.1.4

管理计算资源

让我们总结一下本章涵盖的组件,这些是您在部署 VM 之前需要的要求。对于存储账户来说,这不是一个真正的要求,但是您想在遇到麻烦时无法接收引导诊断吗?如前所述,如果您的 VM 进入非可引导状态,引导诊断账户非常有用。存储在此账户中的日志可用于查找 VM 非引导状态的根本原因。对于测试来说,这不是一个强制性选项,但对于生产工作负载,建议启用引导诊断,这将帮助您了解故障发生时出了什么问题。

注意

这里提到的每个资源也被 Azure 容器服务和 Azure Kubernetes 服务使用。

如果您还记得,在技术要求部分,我们看了一下使用 PowerShell 代码创建新 VM 的过程,其中大多数变量未定义。这里是代码:

  New-AzVm '
  -ResourceGroupName $myRG '
  -Name $myTestVM '
  -ImageName UbuntuLTS '
  -Location $myLocation '
  -VirtualNetworkName "$myTestVM-Vnet" '
  -SubnetName $myTestVM-Subnet '
  -SecurityGroupName "$myTestVM-NSG" '
  -PublicIpAddressName $myTestVM-pip

现在,我希望您能理解这些参数各自代表什么,以及它们对于您的 VM 有多么重要。

虚拟机资源

在本节中,我们将提供一些表格,其中包含必要的组件以及 PowerShell 和 Bash 中对应的命令。它可以与 PowerShell 中的帮助一起使用(help <cmdlet>),Azure CLI(在命令中添加--help参数),或 Azure 在线文档。

Azure 配置文件

Azure 配置文件包括描述 Azure 环境所需的设置:

描述 Azure 环境所需的 Azure 配置文件设置命令列表

图 4.20:Azure 配置文件设置命令

资源组

资源组需要包含和管理资源:

创建和查看 Azure 资源组的命令列表

图 4.21:Azure 资源组命令

存储账户

如果您想在 VM/容器外部存储数据,则需要存储账户:

用于 Azure 存储账户的命令列表。

图 4.22:Azure 存储账户命令

虚拟网络

虚拟网络用于 VM/容器之间的通信和与外部世界的通信:

可用于创建和查看 Azure 虚拟网络和子网的命令列表。

图 4.23:Azure 虚拟网络命令

网络安全组

NSG 包括访问控制列表ACL)来保护您的工作负载并在需要时允许访问。它与公共 IP 地址一起,也需要用于端口转发到 VM/容器:

用于 Azure 网络安全组的命令列表。

图 4.24:Azure NSG 命令

公共 IP 地址和网络接口

公共 IP 地址提供了从外部世界进入 VM/容器的访问。这对于端口地址转换(PAT)和 SNAT 是必要的:

用于 Azure 公共 IP 地址和网络接口的命令列表。

图 4.25:Azure 公共 IP 地址和网络接口命令

摘要

通过本章获得的知识,您现在应该更好地理解了您在第二章,开始使用 Azure 云中遇到的事物。

在本章中,我们探讨了在您可以在 Azure 中创建工作负载之前所需的所有 Azure 组件:

  • 您需要一个存储账户来存储 VM 之外的数据。

  • 您需要一个存储账户来存储 VM 之外的数据。

  • 需要网络组件才能与 VM 进行通信,启用机器之间的通信,并使 VM 能够访问互联网。

到目前为止,我们讨论的步骤将对您理解与 VM 相关的组件以及这些组件如何在 Azure 中部署非常有用。我们从 Azure 中的存储解决方案开始,然后也涵盖了网络。我们希望这给您一个关于这些组件如何组合提供服务交付的想法。

在下一章中,我们将利用本章获得的知识来识别和配置 Linux 操作系统中的网络和存储组件。除了网络和存储主题,我们还将探讨其他系统管理任务,如软件和服务管理。

问题

  1. 在您创建 VM 之前需要哪些资源?

  2. 哪些资源建议用于 VM?

  3. 在示例中,随机数生成器被多次使用——为什么?

  4. 网络上的AddressPrefix的目的是什么?

  5. 子网上的AddressPrefix的目的是什么?

  6. NSG 的目的是什么?

  7. 为什么需要公共 IP 地址与外部世界进行通信?

  8. 静态和动态分配的公共 IP 地址有什么区别?

进一步阅读

微软出版社的书籍Implementing Microsoft Azure Infrastructure Solutions旨在作为学习 70-533 考试的参考指南;即使考试已经废弃,内容仍然适合参考。它使用 Azure 门户和命令行界面详细解释了 Azure 基础架构的每个部分。

如果您是网络方面的新手,另一本推荐的书籍,也是作为考试学习指南编写的,是 Glen D. Singh 和 Rishi Latchmepersad 的Comptia Network+ Certification Guide

更古老且更难阅读的是 IBM 的免费TCP/IP Redbookwww.redbooks.ibm.com/redbooks/pdfs/gg243376.pdf);它涵盖的内容远远超出您需要了解的范围,但如果您对这个主题感兴趣,这是必读的。即使您对参加思科 ICND1 考试不感兴趣,Neil Anderson 在www.packtpub.com录制了一个视频,除了思科部分外,还提供了对网络的很好介绍。

注意

请注意,Azure 环境不断变化,特别是在存储和网络方面;验证来源与微软网站上可用的文档非常重要。发布日期可能是您首先要检查的内容。

第五章:高级 Linux 管理

第三章,基本 Linux 管理中,涵盖了一些基本的 Linux 命令,并学会了如何在 Linux 环境中找到自己的位置。之后,在第四章,管理 Azure中,我们深入探讨了 Azure 架构。

通过这两章获得的知识,我们现在准备继续我们在 Linux 中的旅程。让我们继续探索以下主题:

  • 软件管理,我们将看到如何向 Linux 机器添加新软件包以及如何更新现有软件包。

  • 存储管理。在上一章中,我们讨论了如何从 Azure 向您的虚拟机VM)附加数据磁盘,但现在我们将讨论如何在 Linux 中管理这些磁盘。

  • 网络管理。之前,我们讨论了如何向 VM 添加网络接口卡NIC)以及在 Azure 中管理网络资源的方法。在本章中,我们将讨论这些资源在 Linux 中是如何管理的。

  • 系统管理,我们将讨论如何管理服务和系统必需品。

技术要求

为了本章的目的,您需要在 Azure 中部署一个 Linux VM,并选择您喜欢的发行版。

在尺寸方面,您至少需要 2GB 的临时存储空间,并且能够添加至少三个额外的磁盘。例如,B2S VM 大小是一个很好的起点。在本章中,我分享了 Ubuntu、Red Hat 和 SUSE 系统的步骤;您可以选择要遵循的发行版。

软件管理

在任何操作系统中,我们需要安装一些软件来帮助我们进行日常工作。例如,如果你正在编写脚本,操作系统自带的股票软件或应用可能不够用。在这种情况下,您需要安装诸如 Visual Studio Code 之类的软件来简化您的工作。同样,在企业环境中,您可能需要添加新软件或甚至更新现有软件以满足业务需求。

在过去,安装软件是将存档提取到文件系统中的事情。然而,这种方法存在一些问题:

  • 如果文件被复制到其他软件也在使用的目录中,那么删除软件就会很困难。

  • 升级软件很困难;也许文件仍在使用中,或者因为某种原因被重命名。

  • 处理共享库很困难。

这就是为什么 Linux 发行版发明了软件管理器。使用这些软件管理器,我们可以安装完成任务所需的软件包和应用程序。以下是一些软件管理器:

  • RPM

  • YUM

  • DNF

  • DPKG

  • APT

  • ZYpp

让我们仔细看看每个内容,并了解它们如何用于管理 Linux 系统中的软件。

RPM 软件管理器

1997 年,Red Hat 发布了他们的软件包管理器 RPM 的第一个版本。其他发行版,如 SUSE,也采用了这个软件包管理器。RPM 是rpm实用程序的名称,也是格式和文件扩展名的名称。

RPM 软件包包含以下内容:

  • 打包的二进制文件和配置文件的CPIOCopy In, Copy Out)存档。CPIO 是一个用于合并多个文件并创建存档的实用程序。

  • 包含有关软件的描述和依赖关系等信息的元数据。

  • 用于预安装和后安装脚本的 Scriptlets。

过去,Linux 管理员使用rpm实用程序在 Linux 系统上安装、更新和删除软件。如果存在依赖关系,rpm命令可以告诉您确切需要安装哪些其他软件包。rpm实用程序无法解决依赖关系或软件包之间可能的冲突。

现在,我们不再使用rpm实用程序来安装或删除软件,尽管它是可用的;相反,我们使用更先进的软件安装程序。在使用yum(在 Red Hat/CentOS)或zypper(在 SUSE)安装软件后,所有元数据都会进入数据库。使用rpm命令查询这个rpm数据库非常方便。

以下是最常见的rpm查询参数列表:

最常用的 rpm 查询参数及其描述列表。

图 5.1:常见的rpm查询参数

以下截图是获取有关已安装 SSH 服务器软件包的信息的示例:

有关已安装 SSH 服务器软件包的信息。

图 5.2:SSH 服务器软件包信息

-V参数的输出可以告诉我们有关已安装软件的更改。让我们对sshd_config文件进行更改:

sudo cp /etc/ssh/sshd_config /tmp
sudo sed -i 's/#Port 22/Port 22/' /etc/ssh/sshd_config

如果验证已安装的软件包,则输出中会添加ST,表示时间戳已更改,文件大小不同:

表示对已安装软件所做更改的一些字符。

图 5.3:S 和 T 表示时间戳和文件大小的更改

输出中的其他可能字符如下:

SSH 服务器输出中可能的字符列表及其解释。

图 5.4:可能的输出字符及其描述

对于文本文件,diff命令可以帮助显示/tmp目录中备份与/etc/ssh目录中配置之间的差异:

sudo diff /etc/ssh/sshd_config /tmp/sshd_config

按以下方式恢复原始文件:

sudo cp /tmp/sshd_config /etc/ssh/sshd_config

使用 YUM 进行软件管理

up2date实用程序。它目前在所有基于 Red Hat 的发行版中使用,但将被 Fedora 使用的dnf替换。好消息是,dnfyum的语法兼容。

YUM 负责以下工作:

  • 安装软件,包括依赖项

  • 更新软件

  • 删除软件

  • 列出和搜索软件

重要的基本参数如下:

用于安装、更新、删除、搜索和列出软件的 YUM 参数列表。

图 5.5:基本 YUM 参数

您还可以安装软件模式;例如,文件和打印服务器模式或组是一种非常方便的方式,可以一次安装网络文件共享NFS)和 Samba 文件服务器以及 Cups 打印服务器,而不是逐个安装软件包:

用于安装、列出、更新和删除软件组的 YUM 组命令列表。

图 5.6:YUM 组命令及其描述

yum的另一个好功能是与历史记录一起工作:

用于列出 YUM 执行的任务或任务内容的 YUM 历史命令列表。列表还包含撤消或重做任务的命令。

图 5.7:YUM 历史命令及其描述

yum命令使用存储库来进行所有软件管理。要列出当前配置的存储库,请使用以下命令:

yum repolist

要添加另一个存储库,您将需要yum-config-manager工具,它在/etc/yum.repos.d中创建和修改配置文件。例如,如果要添加一个存储库以安装 Microsoft SQL Server,请使用以下命令:

yum-config-manager --add-repo \
  https://packages.microsoft.com/config/rhel/7/\
  mssql-server-2017.repo

yum功能可以通过插件进行扩展,例如,选择最快的镜像,启用文件系统/ LVM 快照,并将yum作为定期任务(cron)运行。

使用 DNF 进行软件管理

在 Red Hat Enterprise Linux 8 以及所有基于此发行版和 Fedora 的发行版中,yum命令被 DNF 取代。语法相同,所以只需要替换三个字符。关于yum-config-manager命令,它被替换为dnf config-manager

它与dnf命令本身集成,而不是一个单独的实用程序。

还有新的功能。 RHEL 8 配备了软件模块化,也被称为AppStreams。作为一种打包概念,它允许系统管理员从多个可用版本中选择所需的软件版本。顺便说一句,此刻可能只有一个版本可用,但会有更新的版本出现!例如,其中一个可用的 AppStreams 是 Ruby 编程解释器。让我们来看看这个模块:

sudo dnf module list ruby

Ruby 编程解释器模块指示当前可用版本 2.5。

图 5.8:Ruby 编程解释器模块

从前面的输出中,您可以观察到在撰写本书时,只有版本 2.5 可用;将来会添加更多版本。这是默认版本,但未启用也未安装。

要启用和安装 AppStreams,请执行以下命令:

sudo dnf module enable ruby:2.5
sudo dnf module install ruby

如果再次列出 AppStreams,输出将发生变化:

输出中的 e 和 i 表示 Ruby 2.5 已启用并安装。

图 5.9:已安装并启用 Ruby 2.5

提示:要知道 AppStreams 安装了哪些软件包,可以使用以下命令:

sudo dnf module info ruby

注意

要了解有关订阅管理器的更多信息,请访问access.redhat.com/ecosystem/ccsp/microsoft-azure

DPKG 软件管理器

Debian 发行版不使用 RPM 格式,而是使用 1995 年发明的 DEB 格式。该格式在所有基于 Debian 和 Ubuntu 的发行版上都在使用。

DEB 软件包包含以下内容:

  • 一个名为debian-binary的文件,其中包含软件包的版本。

  • 一个名为control.tar的存档文件,其中包含元数据(软件包名称、版本、依赖项和维护者)。

  • 一个名为data.tar的存档文件,其中包含实际软件。

DEB 软件包的管理可以使用dpkg实用程序完成。与rpm一样,dpkg实用程序虽然具有安装软件的功能,但已不再使用。取而代之的是更先进的apt命令。尽管如此,了解 dpkg 命令的基础知识仍然是很有用的。

所有元数据都存储在一个可以使用dpkgdpkg-query查询的数据库中。

dpkg-query的重要参数如下:

用于列出已安装软件包中的文件并显示软件包信息和状态的 dpkg-query 参数的列表。

图 5.10:重要的dpkg-query参数

dpkg -l输出的第一列还显示了软件包是否已安装,或未安装,或未解包,或半安装等等:

在 dpkg -l 命令的输出中,ii 表示软件包已安装。

图 5.11:dpkg -l命令的输出

第一列中的第一个字符是所需的操作,第二个字符是软件包的实际状态,可能的第三个字符表示错误标志(R)。ii表示软件包已安装。

可能的所需状态如下:

  • u: 未知

  • i:安装

  • h:保持

  • r: 删除

  • p:清除

重要的软件包状态如下:

  • n:未—软件包未安装。

  • i:已安装—软件包已成功安装。

  • c:Cfg-files—配置文件存在。

  • u:未解包—软件包仍未解包。

  • f:失败-cfg—无法删除配置文件。

  • h:半安装—软件包只安装了一部分。

使用 apt 进行软件管理

在基于 Debian/Ubuntu 的发行版中,软件管理是通过apt实用程序完成的,这是apt-getapt-cache实用程序的最新替代品。

最常用的命令包括以下内容:

常见 apt 命令及其用途的列表。

图 5.12:常见的apt命令及其描述

存储库配置在/etc/apt/sources.list目录中,文件位于/etc/apt/sources.list.d/目录中。另外,还可以使用apt-add-repository命令:

apt-add-repository \
  'deb http://myserver/path/to/repo stable'

apt存储库具有发行类的概念,其中一些列在此处:

  • oldstable:软件已在先前版本的发行版中进行了测试,但尚未为当前版本进行测试。

  • stable:软件已正式发布。

  • testing:软件尚未稳定,但正在进行中。

  • unstable:软件开发正在进行,主要由开发人员运行。

存储库还具有组件的概念,也称为主要存储库:

  • main:经过测试并提供支持和更新

  • contrib:经过测试并提供支持和更新,但有一些依赖项不在主要存储库中,而是在non-free

  • non-free:不符合 Debian 社交契约指南的软件(www.debian.org/social_contract#guidelines

Ubuntu 添加了几个额外的组件或存储库:

  • Universe:由社区提供,无支持,可能有更新

  • Restricted:专有设备驱动程序

  • Multiverse:受版权或法律问题限制的软件

使用 ZYpp 进行软件管理

SUSE 与 Red Hat 一样,使用 RPM 进行软件包管理。但是,他们使用另一个工具集与 ZYpp(也称为 libzypp)作为后端,而不是使用yum。软件管理可以使用图形配置软件 YaST 或命令行界面工具 Zypper 来完成。

注意

YUM 和 DNF 也可以在 SUSE 软件存储库中使用。您可以使用它们来管理(仅限于安装和删除)本地系统上的软件,但这不是它们可用的原因。原因是 Kiwi:一个用于构建操作系统映像和安装程序的应用程序。

重要的基本参数如下:

用于安装、升级、搜索、删除、更新软件并提供软件信息的 Zypper 命令列表。

图 5.13:重要的 Zypper 命令及其描述

有一个搜索选项可以搜索命令,what-provides,但它非常有限。如果您不知道软件包名称,可以使用名为cnf的实用程序。在使用cnf之前,您需要安装scout;这样可以搜索软件包属性:

sudo zypper install scout

之后,您可以使用cnf

使用 cnf 实用程序显示将安装的软件以及数据使用详细信息。

图 5.14:使用 cnf 实用程序

如果您想将系统更新到新的发行版版本,您必须首先修改存储库。例如,如果您想从基于SUSE Linux 企业服务器SLES)的 SUSE LEAP 42.3 更新到基于SUSE Linux 企业SLE)的 15.0 版本,执行以下过程:

  1. 首先,安装当前版本的可用更新:
sudo zypper update
  1. 更新到 42.3.x 版本的最新版本:
sudo zypper dist-upgrade  
  1. 修改存储库配置:
sudo sed -i 's/42.3/15.0/g' /etc/zypp/repos.d/*.repo
  1. 初始化新的存储库:
sudo zypper refresh
  1. 安装新的发行版:
sudo zypper dist-upgrade

当然,在发行版升级后,您必须重新启动。

除了安装软件包,您还可以安装以下内容:

  • patterns:软件包组,例如,安装包括 PHP 和 MySQL 的完整 Web 服务器(也称为 LAMP)

  • patches:软件的增量更新

  • products:安装额外产品

要列出可用的模式,请使用以下命令:

zypper patterns

要安装它们,请使用以下命令:

sudo zypper install --type pattern <pattern>

相同的过程适用于补丁和产品。

Zypper 使用在线存储库查看当前配置的存储库:

sudo zypper repos

您可以使用addrepo参数添加存储库;例如,要在 LEAP 15.0 上添加最新 PowerShell 版本的社区存储库,请执行以下命令:

sudo zypper addrepo \
  https://download.opensuse.org/repositories\
  /home:/aaptel:/powershell-stuff/openSUSE_Leap_15.0/\
  home:aaptel:powershell-stuff.repo 

如果您添加存储库,您总是需要刷新存储库:

sudo zypper refresh

注意

SUSE 有可信或不可信的存储库的概念。如果供应商不受信任,您需要在install命令中添加--from参数。或者,您可以在/etc/vendors.d中添加配置文件,如下所示:

[main]

vendors = suse,opensuse,obs://build.suse.de

可以使用zypper info找到软件包的供应商。

现在您知道如何管理分发中的软件了,让我们继续讨论网络。在上一章中,我们讨论了 Azure 中的网络资源;现在是时候了解 Linux 网络了。

网络

在 Azure 中,网络设置(例如 IP 地址和 DNS 设置)是通过动态主机配置协议DHCP)提供的。配置与在其他平台上运行的物理机器或虚拟机的配置非常相似。不同之处在于配置由 Azure 提供,通常不应更改。

在本节中,您将学习如何识别 Linux 中的网络配置,以及如何将该信息与上一章中介绍的 Azure 中的设置相匹配。

识别网络接口

在引导过程中和之后,Linux 内核负责硬件识别。当内核识别硬件时,它将收集的信息交给一个名为systemd-udevd的进程,即运行的守护进程(后台进程)。此守护进程执行以下操作:

  • 必要时加载网络驱动程序。

  • 它可以负责设备命名。

  • 使用所有可用信息更新/sys

udevadm实用程序可以帮助您显示硬件标识。您可以使用udevadm info命令查询设备信息的udev数据库:

使用 udevadm info 命令查询 udev 数据库的设备信息。

图 5.15:使用udevadm info命令检索设备信息

除了使用udevadm之外,还可以访问/sys/class/net目录,并使用可用文件查看cat命令,但这不是一个非常用户友好的方法,通常不需要这样做,因为有解析所有可用信息的实用程序。

最重要的实用程序是ip命令。让我们从列出可用的网络接口和相关信息开始:

ip link show

上述命令应该给出以下输出:

使用 ip link show 命令列出可用的网络接口及其信息。

列出可用的网络接口后,您可以更具体:

ip link show dev eth0

所有状态标志的含义,如LOWER_UP,可以在man 7 netdevice中找到。

识别 IP 地址

在了解网络接口的名称后,可以使用ip实用程序显示网络接口上配置的 IP 地址,如下截图所示:

使用 ip 实用程序命令显示网络接口上配置的 IP 地址。

图 5.17:使用 ip 实用程序检索配置的 IP 地址

显示路由表

路由表是存储在 Linux 内核中的结构,其中包含有关如何路由数据包的信息。您可以配置路由表,并根据规则或条件使数据包采取某条路线。例如,您可以声明如果数据包的目的地是 8.8.8.8,则应将其发送到网关。路由表可以按设备或子网显示:

使用 ip route show 命令显示路由表。

图 5.18:显示路由表

另一个不错的功能是您可以查询用于到达特定 IP 的设备和网关:

使用 ip route get 命令查询要使用的设备和网关。

图 5.19:查询用于特定 IP 的设备和网关

网络配置

现在我们知道如何识别接口的 IP 地址和接口定义的路由,让我们看看这些 IP 地址和路由如何在 Linux 系统上配置。

ip命令主要用于验证设置。持久配置通常由另一个守护程序管理。不同的发行版有不同的管理网络的守护程序:

  • RHEL 发行版使用NetworkManager

  • 在 SLE 和 OpenSUSE LEAP 中,使用wicked

  • 在 Ubuntu 17.10 及更高版本中,使用systemd-networkdsystemd-resolved,而早期版本的 Ubuntu 完全依赖于/etc/network/interfaces.d/*cfg文件中配置的 DHCP 客户端。

在 Ubuntu 中,Azure Linux Guest Agent 在/run/system/network目录中创建两个文件。一个是名为10-netplan-eth0.link的链接文件,用于保留基于 MAC 地址的设备名称:

[Match]
MACAddress=00:....
[Link]
Name=eth0
WakeOnLan=off

另一个是10-netplan-eth0.network,用于实际网络配置:

[Match]
MACAddress=00:...
Name=eth0
[Network]
DHCP=ipv4
[DHCP]
UseMTU=true
RouteMetric=100

如果有多个网络接口,将创建多组文件。

在 SUSE 中,Azure Linux Guest Agent 创建一个名为/etc/sysconfig/network/ifcfg-eth0的文件,其中包含以下内容:

BOOTPROTO='dhcp'
DHCLIENT6_MODE='managed'
MTU=''
REMOTE_IPADDR=''
STARTMODE='onboot'
CLOUD_NETCONFIG_MANAGE='yes'

wicked守护程序读取此文件并将其用于网络配置。与 Ubuntu 一样,如果有多个网络接口,则会创建多个文件。可以使用wicked命令查看配置的状态:

wicked show 命令提供网络配置状态详细信息。

图 5.20:使用 wicked show 命令检查配置状态

在 RHEL 和 CentOS 中,ifcfg-文件创建在/etc/sysconfig/network-scripts目录中:

DEVICE=eth0
ONBOOT=yes
BOOTPROTO=dhcp
TYPE=Ethernet
USERCTL=no
PEERDNS=yes
IPV6INIT=no
NM_CONTROLLED=no
DHCP_HOSTNAME=...

如果NM_CONTROLLED设置为no,则NetworkManager将无法控制连接。大多数 Azure Linux 机器都将其设置为yes;尽管如此,您可以从/etc/sysconfig/network-scripts目录中的ifcfg-文件中验证它。您可以使用nmcli命令显示设备设置,但不能使用该命令修改这些设置:

nmcli 命令用于显示设备设置的完整信息。

图 5.21:使用nmcli命令显示设备设置

网络配置更改

如前所述,Azure DHCP 服务器提供了每个网络设置。到目前为止,我们学到的一切都是关于验证在 Azure 中配置的网络设置。

如果在 Azure 中更改了某些内容,则需要在 Linux 中重新启动网络。

在 SUSE 和 CentOS 中,可以使用以下命令执行此操作:

sudo systemctl restart network

在 Ubuntu Server 的最新版本中,使用以下命令:

sudo systemctl restart systemd-networkd
sudo systemctl restart systems-resolved

主机名

可以使用hostnamectl实用程序找到 VM 的当前主机名:

使用 hostnamecl 实用程序获取主机名详细信息。

图 5.22:使用hostnamectl实用程序获取主机名

主机名由 Azure 中的 DHCP 服务器提供;要查看 Azure 中配置的主机名,可以使用 Azure 门户、Azure CLI 或 PowerShell。例如,在 PowerShell 中,使用以下命令:

$myvm=Get-AzVM -Name CentOS-01 '
  -ResourceGroupName MyResource1
$myvm.OSProfile.ComputerName

在 Linux 中,可以使用hostnamectl实用程序更改主机名:

sudo hostnamectl set-hostname <hostname>
sudo systemctl restart waagent #RedHat & SUSE
sudo systemctl restart walinuxagent  #Ubuntu

这应该更改您的主机名。如果不起作用,请检查 Azure Linux VM 代理的配置文件/etc/waagent.conf

Provisioning.MonitorHostName=y

如果仍然无法正常工作,请编辑/var/lib/waagent/ovf-env.xml文件,并更改HostName参数。另一个可能的原因是ifcfg-<interface>文件中的DHCP_HOSTNAME行;只需删除它并重新启动NetworkManager

DNS

DNS 设置也是通过 Azure DHCP 服务器提供的。在 Azure 中,这些设置附加到虚拟网络接口上。您可以在 Azure 门户、PowerShell(Get-AZNetworkInterface)或 Azure CLI(az vm nic show)中查看它们。

当然,您可以配置自己的 DNS 设置。在 PowerShell 中,声明 VM 并标识网络接口:

$myvm = Get-AzVM -Name <vm name> '
  -ResourceGroupName <resource group>
$nicid = $myvm.NetworkProfile.NetworkInterfaces.Id

最后一个命令将为您提供所需网络接口的完整 ID;此 ID 的最后部分是接口名称。现在让我们从输出中剥离它并请求接口属性:

$nicname = $nicid.split("/")[-1]
$nic = Get-AzNetworkInterface '
  -ResourceGroupName <resource group> -Name $nicname 
$nic

如果查看$nic变量的值,您会发现它包含了我们需要的所有信息:

使用$nic 变量列出网络接口属性。

图 5.23:使用$nic 变量获取接口属性

最后一步是更新 DNS 域名服务器设置。为了本书的目的,我们使用的是9.9.9.9,这是一个名为 Quad9 的公共、免费可用的 DNS 服务。您也可以使用谷歌的 DNS 服务(8.8.8.88.8.4.4):

$nic.DnsSettings.DnsServers.Add("9.9.9.9")
$nic | Set-AzNetworkInterface
$nic | Get-AzNetworkInterface | '
  Select-Object -ExpandProperty DnsSettings

使用 Azure CLI 的方法类似,但涉及的步骤较少。搜索网络接口名称:

nicname=$(az vm nic list \
  --resource-group <resource group> \
  --vm-name <vm name> --query '[].id' -o tsv | cut –d "/" -f9)

更新 DNS 设置:

az network nic update -g MyResource1 --name $nicname \
  --dns-servers 9.9.9.9

然后验证新的 DNS 设置:

az network nic show --resource-group <resource group> \
  --name $nicname --query "dnsSettings"

在 Linux VM 中,您必须更新 DHCP 租约以接收新的设置。为了做到这一点,您可以在 RHEL 中运行systemctl restart NetworkManager或在 Ubuntu 中运行dhclient -r。设置保存在/etc/resolv.conf文件中。

在使用systemd的网络实现的 Linux 发行版中,如 Ubuntu,/etc/resolv.conf文件是指向/run/systemd/resolve/目录中的文件的符号链接,sudo systemd-resolve --status命令会显示当前的设置:

link 2 (eth0)
      Current Scopes: DNS
       LLMNR setting: yes
MulticastDNS setting: no
      DNSSEC setting: no
    DNSSEC supported: no
         DNS Servers: 9.9.9.9
          DNS Domain: reddog.microsoft.com

要测试 DNS 配置,可以使用dig,或者更简单的host实用程序,如下所示:

dig www.google.com A

存储

在上一章中,我们讨论了如何创建磁盘并将其附加到 VM,但我们的工作并不止于此。我们必须对 Linux 机器进行分区或挂载磁盘。在本节中,我们将讨论 Linux 中的存储管理。Azure 中有两种类型的存储可用:附加到 VM 的虚拟磁盘和 Azure 文件共享。在本章中,将涵盖这两种类型。我们将讨论以下主题:

  • 向 VM 添加单个虚拟磁盘

  • 处理文件系统

  • 使用逻辑卷管理器LVM)和 RAID 软件处理多个虚拟磁盘

由块设备提供的存储

块设备可以提供本地和远程存储。在 Azure 中,几乎总是附加到 VM 的虚拟硬盘,但也可以使用Internet 小型计算机系统接口iSCSI)卷,由 Microsoft Azure StorSimple 或第三方提供。

附加到 VM 的每个磁盘都由内核标识,并在标识后,内核将其交给一个名为systemd-udevd的守护进程。这个守护进程负责在/dev目录中创建一个条目,更新/sys/class/block,并在必要时加载一个驱动程序来访问文件系统。

/dev中的设备文件提供了一个简单的接口给块设备,并由 SCSI 驱动访问。

识别可用块设备的多种方法。一种可能性是使用lsscsi命令:

可用块设备列表。

图 5.24:使用lsscsi命令识别块设备

第一个可用的磁盘称为sda—SCSI 磁盘 A。此磁盘是从在 VM 配置期间使用的映像磁盘创建的,也称为根磁盘。您可以通过/dev/sda/dev/disk/azure/root访问此磁盘。

识别可用存储的另一种方法是使用lsblk命令。它可以提供有关磁盘内容的更多信息:

使用 lsblk 命令获取磁盘内容信息。

图 5.25:使用 lsblk 命令识别可用存储

在此示例中,在/dev/sda, sda1sda2(或/dev/disk/azure/root-part1root-part2)上创建了两个分区。第二列中的主要号码8表示这是一个 SCSI 设备;次要部分只是编号。第三列告诉我们该设备不可移动,用0表示(如果可移动,则为1),第五列告诉我们驱动器和分区不是只读的:再次,只读为1,读写为0

另一个可用的磁盘是资源磁盘,/dev/sdb/dev/disk/azure/resource),这是一个临时磁盘。这意味着数据不是持久的,在重新启动后会丢失,并且用于存储诸如页面或交换文件之类的数据。交换就像 Windows 中的虚拟内存,当物理 RAM 已满时使用。

添加数据磁盘

在本节中,我们将回顾我们在上一章中所做的工作,以继续练习并帮助您熟悉命令。如果您已经添加了数据磁盘的 VM,可以跳过本节。

您可以通过 Azure 门户或通过 PowerShell 向 VM 添加额外的虚拟磁盘。让我们添加一个磁盘:

  1. 首先,声明我们要如何命名我们的磁盘以及磁盘应该创建在哪里:
$resourcegroup = '<resource group>'
$location = '<location>'
$diskname = '<disk name>'
$vm = Get-AzVM '
  -Name <vm name> '
  -ResourceGroupName $resourcegroup
  1. 创建虚拟磁盘配置-大小为 2GB 的空标准托管磁盘:
 $diskConfig = New-AzDiskConfig '
   -SkuName 'Standard_LRS' '
   -Location $location '
   -CreateOption 'Empty' '
   -DiskSizeGB 2
  1. 使用此配置创建虚拟磁盘:
$dataDisk1 = New-AzDisk '
  -DiskName $diskname '
  -Disk $diskConfig '
  -ResourceGroupName $resourcegroup
  1. 将磁盘附加到 VM:
$vmdisk = Add-AzVMDataDisk '
  -VM $vm -Name $diskname '
  -CreateOption Attach '
  -ManagedDiskId $dataDisk1.Id '
  -Lun 1
Update-AzVM '
  -VM $vm '
  -ResourceGroupName $resourcegroup
  1. 当然,您也可以使用 Azure CLI:
az disk create \
  --resource-group <resource group> \
  --name <disk name> \
  --location <location> \
  --size-gb 2 \
  --sku Standard_LRS \ 

az vm disk attach \
  --disk <disk name> \
  --vm-name <vm name> \
  --resource-group <resource group> \ 
  --lun <lun number>

注意

LUN 是逻辑单元编号的缩写,用于标记存储(在我们的情况下是虚拟存储)的编号或标识符,这将帮助用户区分存储。您可以从零开始编号。

创建虚拟磁盘后,它将作为/dev/sdc/dev/disk/azure/scsi1/lun1)显示在 VM 中。

rescan-scsi-bus命令,它是sg3_utils软件包的一部分。

再次查看lssci的输出:

[5:0:0:1]    disk   Msft     Virtual Disk     1.0  /dev/sdc

第一列格式化为:

<hostbus adapter id> :  <channel id> : <target id> : <lun number>

hostbus adapter是与存储的接口,并由 Microsoft Hyper-V 虚拟存储驱动程序创建。通道 ID 始终为0,除非您已配置多路径。目标 ID 标识控制器上的 SCSI 目标;对于 Azure 中的直接连接设备,这始终为零。

分区

在您可以使用块设备之前,您需要对其进行分区。有多种可用于分区的工具,一些发行版配备了自己的实用程序来创建和操作分区表。例如,SUSE 在其 YaST 配置工具中有一个。

在本书中,我们将使用parted实用程序。这在每个 Linux 发行版上都是默认安装的,并且可以处理所有已知的分区布局:msdosgptsun等。

您可以从命令行以脚本方式使用parted,但是,如果您对parted不熟悉,最好使用交互式 shell:

parted /dev/sdc
  GNU Parted 3.1
  Using /dev/sdc
  Welcome to GNU Parted! Type 'help' to view a list of commands.
  1. 第一步是显示有关此设备的可用信息:
(parted) print
  Error: /dev/sdc: unrecognised disk label
  Model: Msft Virtual Disk (scsi)
  Disk /dev/sdc: 2147MB
  Sector size (logical/physical): 512B/512B
  Partition Table: unknown
  Disk Flags:

这里的重要一行是unrecognised disk label。这意味着没有创建分区布局。如今,最常见的布局是parted在问号后支持自动补全-按两次Ctrl + I

  1. 将分区标签更改为gpt
(parted) mklabel
 New disk label type? gpt
  1. 通过再次打印磁盘分区表来验证结果:
(parted) print
  Model: Msft Virtual Disk (scsi)
  Disk /dev/sdc: 2147MB
  Sector size (logical/physical): 512B/512B
  Partition Table: gpt
  Disk Flags:
  Number Start  End  Size File system  Name  Flags
  1. 下一步是创建分区:
(parted) mkpart
  Partition name?  []? lun1_part1
  File system type?  [ext2]? xfs
  Start? 0%
  End? 100%

文件系统将在本章后面介绍。对于大小,您可以使用百分比或固定大小。一般来说,在 Azure 中,使用整个磁盘更有意义。

  1. 再次打印磁盘分区表:
(parted) print
  Model: Msft Virtual Disk (scsi)
  Disk /dev/sdc: 2147MB
  Sector size (logical/physical): 512B/512B
  Partition Table: gpt
  Disk Flags:
  Number Start   End     Size   File system  Name        Flags
  1      1049kB 2146MB  2145MB               lun1_part1

请注意,文件系统列仍为空,因为分区尚未格式化。

  1. 使用Ctrl + D,或quit,退出parted

Linux 中的文件系统

文件系统有它们自己的数据组织机制,这将因文件系统而异。如果我们比较可用的文件系统,我们会发现有些快速,有些设计用于更大的存储,有些设计用于处理更小的数据块。您选择的文件系统应取决于最终需求以及您要存储的数据类型。Linux 支持许多文件系统——本地 Linux 文件系统,如 ext4 和 XFS,以及第三方文件系统,如 FAT32。

每个发行版都支持本地文件系统 ext4 和 XFS;除此之外,SUSE 和 Ubuntu 还支持非常现代的文件系统:BTRFS。Ubuntu 是为数不多支持 ZFS 文件系统的发行版之一。

格式化文件系统后,您可以将其挂载到根文件系统。mount命令的基本语法如下:

mount <partition> <mountpoint>

分区可以使用设备名称、标签或mount命令或通过zfs实用程序进行命名。

另一个重要的文件系统是交换文件系统。除了普通文件系统外,还有其他特殊文件系统:devfs、sysfs、procfs 和 tmpfs。

让我们从对文件系统及其周围实用程序的简短描述开始。

ext4 文件系统

ext4 是一种本地 Linux 文件系统,作为 ext3 的继任者开发,多年来一直是(对于一些发行版来说仍然是)默认文件系统。它提供了稳定性、高容量、可靠性和性能,同时需要最少的维护。除此之外,您可以轻松调整(增加/减少)文件系统的大小。

好消息是它可以在非常低的要求下提供这一点。当然,也有坏消息:它非常可靠,但无法完全保证数据的完整性。如果数据在磁盘上已损坏,ext4 既无法检测到也无法修复这种损坏。幸运的是,由于 Azure 的基础架构,这种情况不会发生。

ext4 并不是最快的文件系统,但对于许多工作负载来说,ext4 和竞争对手之间的差距非常小。

最重要的实用程序如下:

  • mkfs.ext4:格式化文件系统

  • e2label:更改文件系统的标签

  • tune2fs:更改文件系统的参数

  • dump2fs:显示文件系统的参数

  • resize2fs:更改文件系统的大小

  • fsck.ext4:检查和修复文件系统

  • e2freefrag:报告碎片情况

  • e4defrag:对文件系统进行碎片整理;通常不需要

要创建 ext4 文件系统,请使用以下命令:

sudo mkfs.ext4 -L <label> <partition>

标签是可选的,但可以更容易地识别文件系统。

XFS 文件系统

XFS 是一种高度可扩展的文件系统。它可以扩展到 8 EiB(exbibyte = 2⁶⁰ 字节)并且可以在线调整大小;只要有未分配的空间,文件系统就可以增长,并且可以跨多个分区和设备。

XFS 是最快的文件系统之一,特别是与 RAID 卷结合使用时。但这是有代价的:如果要使用 XFS,您的虚拟机至少需要 1 GB 的内存。如果要能够修复文件系统,您至少需要 2 GB 的内存。

XFS 的另一个好功能是您可以使流量静止到文件系统,以创建一致的备份,例如数据库服务器。

最重要的实用程序如下:

  • mkfs.xfs:格式化文件系统

  • xfs_admin:更改文件系统的参数

  • xfs_growfs:减小文件系统的大小

  • xfs_repair:检查和修复文件系统

  • xfs_freeze:暂停对 XFS 文件系统的访问;这使得一致的备份更容易

  • xfs_copy:快速复制 XFS 文件系统的内容

要创建 XFS 文件系统,请使用以下命令:

sudo mkfs.xfs -L <label> <partition>

标签是可选的,但可以更容易地识别文件系统。

ZFS 文件系统

ZFS 是由 SUN 开发的组合文件系统和逻辑卷管理器,自 2005 年以来由 Oracle 拥有。它以出色的性能和丰富的功能而闻名:

  • 卷管理和 RAID

  • 防止数据损坏

  • 数据压缩和重复数据删除

  • 可扩展至 16 艾字节

  • 能够导出文件系统

  • 快照支持

ZFS 可以在 Linux 上使用用户空间驱动程序(FUSE)或 Linux 内核模块(OpenZFS)实现。在 Ubuntu 中,最好使用内核模块;它的性能更好,并且没有 FUSE 实现的一些限制。例如,如果使用 FUSE,则无法通过 NFS 导出文件系统。

OpenZFS 没有被广泛采用的主要原因是许可证问题。OpenZFS 的公共开发和分发许可证CDDL)许可证与 Linux 内核的通用公共许可证不兼容。另一个原因是 ZFS 可能会占用大量内存;您的虚拟机每 TB 存储需要额外 1GB 内存,这意味着 16TB 存储需要 16GB RAM 用于应用程序。对于 ZFS,至少建议使用 1GB 内存。但内存越多越好,因为 ZFS 使用大量内存。

最重要的实用程序如下:

  • zfs:配置 ZFS 文件系统

  • zpool:配置 ZFS 存储池

  • zfs.fsck:检查和修复 ZFS 文件系统

在本书中,仅涵盖了 ZFS 的基本功能。

Ubuntu 是唯一支持 ZFS 的发行版。要在 Ubuntu 中使用 ZFS,您必须安装 ZFS 实用程序:

sudo apt install zfsutils-linux

安装后,您可以开始使用 ZFS。假设您向虚拟机添加了三个磁盘。使用 RAID 0 是个好主意,因为它比单个磁盘提供更好的性能和吞吐量。

首先,让我们使用两个磁盘创建一个池:

sudo zpool create -f mydata /dev/sdc /dev/sdd
sudo zpool list mydata
sudo zpool status mydata

现在让我们添加第三个磁盘,以展示如何扩展池:

sudo zpool add mydata /dev/sde
sudo zpool list mydata
sudo zpool history mydata

您可以直接使用此池,或者您可以在其中创建数据集,以便更精细地控制诸如配额之类的功能:

sudo zfs create mydata/finance
sudo zfs set quota=5G mydata/finance
sudo zfs list

最后,您需要挂载此数据集才能使用它:

sudo zfs set mountpoint=/home/finance mydata/finance
findmnt /home/finance

此挂载将在重新启动后保持。

BTRFS 文件系统

BTRFS 是一个相对较新的文件系统,主要由 Oracle 开发,但也得到了 SUSE 和 Facebook 等公司的贡献。

就功能而言,它与 ZFS 非常相似,但正在大力开发中。这意味着并非所有功能都被认为是稳定的。在使用此文件系统之前,请访问btrfs.wiki.kernel.org/index.php/Status

内存要求与 XFS 相同:虚拟机中需要 1GB 内存。如果要修复文件系统,则不需要额外的内存。

在本书中,仅涵盖了 BTRFS 的基本功能。您可以在所有发行版上使用 BTRFS,但请注意,在 RHEL 和 CentOS 上,文件系统被标记为废弃,在 RHEL 8 中已删除。有关更多信息,请访问access.redhat.com/solutions/197643

最重要的实用程序如下:

  • mkfs.btrfs:使用此文件系统格式化设备

  • btrfs:管理文件系统

假设您向虚拟机添加了三个磁盘。使用 RAID 0 可以提高性能,并允许与仅使用单个磁盘相比获得更高的吞吐量。

首先,让我们使用两个基础磁盘创建一个 BTRFS 文件系统:

sudo mkfs.btrfs -d raid0 -L mydata /dev/sdc /dev/sdd

当然,您可以使用第三个磁盘扩展文件系统,但在这样做之前,您必须挂载文件系统:

sudo mkdir /srv/mydata
sudo mount LABEL=mydata /srv/mydata 
sudo btrfs filesystem show /srv/mydata

现在,添加第三个磁盘:

sudo btrfs device add /dev/sde /srv/mydata
sudo btrfs filesystem show /srv/mydata

像 ZFS 一样,BTRFS 有数据集的概念,但在 BTRFS 中,它们被称为子卷。要创建一个子卷,执行以下命令:

sudo btrfs subvolume create /srv/mydata/finance
sudo btrfs subvolume list /srv/mydata

您可以独立于根卷挂载子卷:

sudo mkdir /home/finance
sudo mount -o subvol=finance LABEL=mydata /home/finance

您可以在findmnt命令的输出中看到 ID258

列出已挂载子卷的 ID。

图 5.26:创建子卷

交换文件系统

如果您的应用程序没有足够的可用内存,可以使用交换。即使机器上有大量 RAM,使用交换也是一个好习惯。

空闲内存是以前使用过但当前应用程序不需要的内存。如果这些空闲内存在长时间内没有使用,它将被交换以使更多的内存可用于更频繁使用的应用程序。

为了提高整体性能,最好为 Linux 安装一些交换空间。最好使用最快的可用存储,最好是在资源磁盘上。

注意

在 Linux 中,您可以使用交换文件和交换分区。性能上没有区别。在 Azure 中,您不能使用交换分区;这将使您的系统不稳定,这是由底层存储引起的。

在 Azure 中,交换由 Azure VM Agent 管理。您可以验证ResourceDisk.EnableSwap参数是否设置为y,以确认在/etc/waagent.conf中启用了交换。此外,您可以在ResourceDisk.SwapSizeMB中检查交换大小:

# Create and use swapfile on resource disk.
ResourceDisk.EnableSwap=y
# Size of the swapfile.
ResourceDisk.SwapSizeMB=2048

一般来说,2048 MB 的swapfile内存已经足够增加整体性能。如果交换没有启用,要创建交换文件,可以通过设置以下三个参数更新/etc/waagent.conf文件:

  • ResourceDisk.Format=y

  • ResourceDisk.EnableSwap=y

  • ResourceDisk.SwapSizeMB=xx

要重新启动 Azure VM Agent,对于 Debian/Ubuntu,执行以下命令:

sudo systemctl restart walinuxagent

对于 Red Hat/CentOS,执行以下命令:

service waagent restart 

验证结果:

ls -lahR /mnt | grep -i swap
swapon –s

如果发现交换文件未创建,可以继续重启 VM。要执行此操作,请使用以下任一命令:

shutdown -r now init 6

Linux 软件 RAID

独立磁盘冗余阵列RAID),最初称为廉价磁盘冗余阵列,是一种冗余技术,相同的数据存储在不同的磁盘中,这将帮助您在磁盘故障的情况下恢复数据。RAID 有不同的级别可用。微软在docs.microsoft.com/en-us/azure/virtual-machines/linux/configure-raid中正式声明,您需要 RAID 0 以获得最佳性能和吞吐量,但这不是强制性的实施。如果您当前的基础架构需要 RAID,那么您可以实施它。

如果您的文件系统不支持 RAID,可以使用 Linux 软件 RAID 来创建 RAID 0 设备。您需要安装mdadm实用程序;它在每个 Linux 发行版上都可用,但可能不是默认安装的。

假设您向 VM 添加了三个磁盘。让我们创建一个名为/dev/md127的 RAID 0 设备(只是一个尚未使用的随机数):

sudo mdadm --create /dev/md127 --level 0 \
  --raid-devices 3 /dev/sd{c,d,e} 

验证配置如下:

cat /proc/mdstat
sudo mdadm --detail /dev/md127

前面的命令应该给出以下输出:

验证/dev/md127 RAID 0 设备的配置。

图 5.27:验证 RAID 配置

使配置持久化:

mdadm --detail --scan --verbose >> /etc/mdadm.conf

现在,您可以使用此设备并使用文件系统格式化它,如下所示:

mkfs.ext4 -L BIGDATA /dev/md127

Stratis

Stratis 是在 RHEL 8 中新引入的,用于创建多磁盘、多层存储池,以便轻松监视和管理该池,并且只需最少量的手动干预。它不提供 RAID 支持,但它将多个块设备转换为一个具有文件系统的池。Stratis 使用已经存在的技术:LVM 和 XFS 文件系统。

如果在您的 RHEL 上未安装 Stratis,可以通过执行以下命令轻松安装:

sudo dnf install stratis-cli

启用守护程序的命令如下:

sudo systemctl enable --now stratisd

假设您向 VM 添加了两个数据磁盘:/dev/sdc/dev/sdd。创建池:

sudo stratis pool create stratis01 /dev/sdc /dev/sdd

使用此命令进行验证:

sudo stratis pool list

输出显示了总存储量;在上面的示例中,为 64 GB。其中 104 MiB 已被占用,用于池管理所需的元数据:

显示 stratis 池中的总物理内存和已使用物理内存。

图 5.28:stratis 池的存储详细信息

要获取有关池中磁盘和使用情况的更多详细信息,请执行以下命令:

sudo stratis blockdev list

如你在以下截图中所见,我们得到了相同的输出,但是有关池中磁盘和使用情况的更多详细信息。在以下输出中,你可以看到池的名称和状态:

获取磁盘名称和正在使用的数据的信息。

图 5.29:池名称和状态

在这里,存储用于数据,因为也可以将磁盘配置为读/写缓存。Stratis 在新创建的池上形成一个文件系统(默认为 xfs):

sudo stratis filesystem create stratis01 finance

文件系统被标记为finance,可以通过设备名称(/stratis/stratis01/finance)或 UUID 进行访问。

有了这些信息,你可以像对待其他文件系统一样挂载它,比如使用 systemd 挂载,我们将在本章后面讨论。

创建文件系统后,可以创建快照,这些快照基本上是原始文件系统的副本。可以通过执行以下命令来添加快照:

sudo stratis filesystem snapshot stratis01 finance finance_snap

要列出文件系统,可以执行以下命令:

sudo stratis filesystem

而且你必须将其挂载为普通文件系统!

添加读/写缓存可以提高性能,特别是如果你使用性能比标准 SSD 磁盘(甚至非 SSD 磁盘)更好的磁盘。假设这个磁盘是/dev/sde

sudo sudo stratis pool add-cache stratis01 /dev/sde

并且以与之前相同的方式进行验证,使用blockdev参数:

将读/写缓存添加到磁盘以提高性能。

图 5.30:向/dev/sde磁盘添加缓存

总之,我们已经讨论了各种文件系统;你的选择将取决于你的需求。首先,你需要确保文件系统与你的发行版兼容;例如,BTRFS 在 RHEL 8 中被移除。因此,在选择之前最好先检查兼容性。

systemd

Linux 内核启动后,第一个 Linux 进程开始第一个进程。这个进程被称为init进程。在现代 Linux 系统中,这个进程是systemd。看一下以下截图,显示了以树状格式显示的运行进程:

使用 systemd 启动的运行进程列表。

图 5.31:以树状格式查看运行进程

systemd 负责在引导过程中并行启动所有进程,除了由内核创建的进程。之后,它按需激活服务等。它还跟踪和管理挂载点,并管理系统范围的设置,比如主机名。

systemd 是一个事件驱动的系统。它与内核通信,并会对事件做出反应,比如时间点或引入新设备或按下Ctrl + Alt + Del的用户。

使用单元

systemd 与单元一起工作,这些单元是由 systemd 管理的实体,并封装了与 systemd 相关的每个对象的信息。

单元文件是包含配置指令的配置文件,描述了单元并定义了其行为。这些文件存储如下:

由 systemd 管理的单元文件列表及其描述。

图 5.32:单元文件及其描述

单元可以通过systemctl实用程序进行管理。如果要查看所有可用类型,执行以下命令:

systemctl --type help

要列出所有已安装的单元文件,请使用以下命令:

sudo systemctl list-unit-files

要列出活动单元,请使用以下命令:

sudo systemctl list-units

list-unit-fileslist-units参数都可以与--type结合使用。

服务

服务单元用于管理脚本或守护程序。让我们来看看 SSH 服务:

注意

这些截图是从 Ubuntu 18.04 中获取的。在其他发行版上,服务的名称可能不同。

使用 systemctl 的状态参数获取服务详细信息。

图 5.33:ssh 服务详细信息

使用systemctlstatus参数,您可以看到该单元已加载,已启用并在启动时启用,并且这是默认值。如果未启用,可以使用此命令启用它;启用将将服务添加到自动启动链中:

sudo systemctl enable <service name.service>

要查看服务的状态,可以执行此命令:

sudo systemctl status <service name>

在输出中,您可以看到 SSH 服务正在运行,并且日志中显示的最后条目如下:

输出表明服务正在运行。

图 5.34:服务状态和条目

要查看unit文件的内容,请执行以下命令:

sudo systemctl cat <service name.service>

unit文件始终有两个或三个部分:

  • [Unit]:描述和依赖项处理

  • [<Type>]:类型的配置

  • [Install]:如果要能够在启动时启用服务,则是可选部分

为了处理依赖项,有几个可用的指令;最重要的是这些:

  • before:在启动此单元之前,指定的单元将被延迟启动。

  • after:在启动此单元之前启动指定的单元。

  • requires:如果激活此单元,则列在此处的单元也将被激活。如果指定的单元失败,此单元也将失败。

  • wanted:如果激活此单元,则列在此处的单元也将被激活。如果指定的单元失败,将没有后果。

注意

如果您不指定beforeafter,则列出的单元或单元(逗号分隔)将与单元同时启动。

ssh服务的示例如下:

[Unit]
Description=OpenSSH Daemon After=network.target
[Service] 
EnvironmentFile=-/etc/sysconfig/ssh 
ExecStartPre=/usr/sbin/sshd-gen-keys-start 
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS 
ExecReload=/bin/kill -HUP $MAINPID KillMode=process
Restart=always
[Install] 
WantedBy=multi-user.target

Service部分中的大多数选项都是不言自明的;如果不是,请查看systemd.unitsystemd.service的手册页。对于[Install]部分,WantedBy指令说明如果启用此服务,它将成为multi-user.target集合的一部分,该集合在启动时激活。

在进入目标之前,最后要涵盖的内容是如何创建覆盖。systemd 单元可以有许多不同的指令;许多是默认选项。要显示所有可能的指令,请执行以下命令:

sudo systemctl show

如果要更改默认值中的一个,请使用以下命令:

sudo systemctl edit <service name.service>

启动编辑器。添加条目,例如如下所示:

[Service]
ProtectHome=read-only

保存更改。您需要重新加载 systemd 配置文件并重新启动服务:

sudo systemctl daemon-reload
sudo systemctl restart sshd

使用systemctl cat sshd.service审查更改。再次登录并尝试在您的主目录中保存一些内容。

注意

如果您想要为systemctl edit添加另一个编辑器,请在/etc/environment文件中添加一个变量SYSTEMD_EDITOR,例如SYSTEMD_EDITOR=/usr/bin/vim

目标

目标是单元的集合。有两种类型的目标:

  • timers.target,其中包含所有计划任务。

  • systemctl isolate <target name.target>,这将关闭不属于目标的所有进程,并启动属于它的所有进程。示例包括rescue.targetgraphical.target单元。

要查看目标的内容,请使用以下命令:

systemctl list-dependencies <target name.target>

计划任务

systemd 可用于安排任务。以下是一个定时器单元文件的示例:

[Unit]
Description=Scheduled backup task
[Timer]
OnCalendar=*-*-* 10:00:00
[Install] 
WantedBy=timers.target

如果将此文件的内容保存到/etc/systemd/system/backup.timer,则需要一个相应的文件,例如/etc/systemd/system/backup.service,其内容如下:

[Unit]
Description = backup script
[Service]
Type = oneshot
ExecStart = /usr/local/bin/mybackup.sh

启用和激活定时器:

sudo systemctl enable --now backup.timer

要了解计划任务的信息,请使用以下命令:

sudo systemctl list-timers

注意

阅读man 7 systemd.time以了解日历事件的语法。该手册页有一个专门的部分介绍了这个。

如果计划任务不是一个重复性的任务,您可以使用以下命令:

sudo systemd-run --on-calendar <event time> <command>

例如,如果我们想要在 2019 年 10 月 11 日上午 12:00 将done回显到文件/tmp/done中,我们必须按照以下屏幕截图所示进行操作:

通过提供事件时间运行非重复性计划任务。

图 5.35:通过提供事件时间运行定时任务

挂载本地文件系统

挂载单元可用于挂载文件系统。挂载单元的名称有一些特殊之处:它必须对应于挂载点。例如,如果你想在/home/finance上挂载,挂载单元文件就变成了/etc/systemd/system/home-finance.mount

[Unit]
Description = Finance Directory
[Mount] 
What = /dev/sdc1
Where = /home/finance
Type = xfs
Options = defaults
[Install]
WantedBy = local-fs.target

使用systemctl start home-finance.mount来开始挂载,使用systemctl enable home-finance.mount来在启动时挂载。

挂载远程文件系统

如果一个文件系统不是本地的而是远程的,例如,如果它是一个 NFS 共享,最好的挂载方式是使用automount。如果你不使用 automount(autofs服务),你必须手动挂载远程共享;这里的优势是,如果你已经访问了远程共享,autofs 会自动挂载。它会挂载共享,如果你失去了与共享的连接,它会尝试按需自动挂载共享。

你需要创建两个文件。让我们以在/home/finance上挂载 NFS 为例。首先,创建/etc/systemd/system/home-finance.mount,内容如下:

[Unit]
Description = NFS Finance Share
[Mount]
What = 192.168.122.100:/share
Where = /home/finance
Type = nfs
Options = vers=4.2

创建一个名为/etc/systemd/system/home-finance.automount的文件:

[Unit]
Description = Automount NFS Finance Share
[Automount]
Where = /home/finance
[Install]
WantedBy = remote-fs.target

启动自动挂载单元,而不是挂载单元。当然,你可以在启动时启用它。

总结

在本章中,我们深入探讨了 Linux,解释了每个 Linux 系统管理员的基本任务:管理软件、网络、存储和服务。

当然,作为 Linux 系统管理员,这不是你每天都会做的事情。很可能你不会手动操作,而是自动化或编排。但是为了能够编排它,你需要了解它的工作原理,并能够验证和排除配置问题。这将在第八章,探索持续配置自动化中介绍。

在下一章中,我们将探索 Linux 中限制对系统访问的选项。

  • 强制访问控制

  • 网络访问控制列表

  • 防火墙

我们还将介绍如何使用 Azure 活动目录域服务将 Linux 机器加入域。

问题

  1. 谁负责硬件的识别?

  2. 谁负责设备命名?

  3. 有哪些识别网络接口的方法?

  4. 谁负责网络配置的维护?

  5. 有哪些识别本地附加存储的方法?

  6. 为什么我们在 Azure 中使用 RAID 0?

  7. 在 Azure 中实现 RAID 0 的选项有哪些?

  8. 尝试使用三个磁盘实现 RAID 0 设备;用 XFS 格式化它。挂载它,并确保它在启动时被挂载。

进一步阅读

在某种程度上,本章是一次深入的探讨,但是关于本章涉及的所有主题,还有很多值得学习的地方。我强烈建议你阅读所有使用的命令的 man 页面。

关于存储,除了 Azure 网站上的文档外,一些文件系统还有它们自己的网站:

Lennart Poettering,systemd 的主要开发人员之一,有一个很好的博客,里面有很多技巧和背景信息:0pointer.net/blog。此外,还有文档可在www.freedesktop.org/wiki/Software/systemd找到。

由于systemctl status命令无法提供足够的信息,我们将在第十一章,故障排除和监控工作负载中进一步讨论日志记录。

第六章:管理 Linux 安全和身份

在上一章中,我们讨论了处理存储、网络和进程管理。然而,作为系统管理员,您的主要目标是保护 Linux 机器,拒绝任何未经授权的访问或限制用户的访问。在企业环境中,安全漏洞是一个巨大的关注点。在本章中,我们将涵盖安全性——在操作系统级别保护您的工作负载;例如,如果您的组织是一个金融机构,在那里您将处理涉及货币承诺甚至客户的个人可识别信息PII)的工作负载,那么保护工作负载以避免任何违规行为就至关重要。当然,Azure 已经为您提供了多种方式和多个层面的服务来保护您的 VM。以下是其中一些服务:

  • Azure 资源管理器,提供安全性、审计和标记功能

  • Web 应用程序防火墙,可防范诸如 SQL 注入等许多攻击

  • 网络安全组的有状态数据包过滤功能

  • Azure 防火墙,提供与 Azure 监控功能紧密集成的有状态防火墙

您还可以订阅 Azure 安全中心服务,进行统一的安全管理,具有许多有吸引力的功能,如持续安全评估。

有了所有这些可能性,我们是否仍然需要在操作系统级别进行保护?在我们看来,多层保护是一个好主意。这将使黑客付出更多的努力和时间,这将使检测黑客变得更容易。没有完全没有漏洞的软件:如果一个应用程序有漏洞,至少操作系统应该受到保护。

身份管理是与安全相关的一个话题。您可以将 Linux 与Azure Active DirectoryAzure AD)集成,以集中管理您的登录帐户,通过使用基于角色的访问控制进行细粒度访问,撤销访问权限,并启用多因素身份验证。

在本章结束时,您将能够:

  • 实施强制访问控制MAC)系统,如 SELinux 或 AppArmor。

  • 了解自主访问控制DAC)的基础知识。

  • 使用 Azure 中可用的身份管理系统。

  • 使用防火墙守护程序和 systemd 增强 Linux 安全性。

Linux 安全提示

在深入讨论您可以采取的所有出色安全措施之前,这里有一些关于安全性的提示。

一般来说,在多个层面上实施安全性是一个好主意。这样,黑客需要不同的方法来获取访问权限,这会花费他们时间。由于这段时间,希望也是由于日志记录和监控,您有更大的机会发现未经授权的访问。

对于文件和目录,尽可能使用suid/sgid位。是否有需要更改自己密码的用户?没有?那么从passwd命令中删除该位。

使用分区,特别是对于/tmp/var/var/tmp/home等目录,并使用noexecnodevnosuid标志进行挂载:

  • 一般来说,允许用户从这些位置执行程序并不是一个好主意。幸运的是,如果你无法将所有者设置为 root,你可以将带有suid位的程序复制到自己的目录中作为普通用户。

  • 这些目录中文件的suidsgid权限非常危险。

  • 不允许在此分区上创建或存在字符或特殊设备。

要访问虚拟机,请使用基于 SSH 密钥的身份验证,而不是密码。使用 ACL 或防火墙限制对特定 IP 的访问。限制用户,并且不允许 root 进行远程访问(使用PermitRootLogin no参数和AllowUsers只允许一个或两个帐户访问)。使用sudo以 root 身份执行命令。也许在sudo配置中创建特殊用户或用户组来执行特殊任务。

不要在虚拟机上安装太多软件,特别是涉及网络服务的软件,比如 Web 服务器和电子邮件服务器。定期使用 ss 命令来查看开放端口,并将其与 ACL 和/或防火墙规则进行比较。

另一个提示是不要在系统上禁用 SELinux,这是 Linux 内核中的一个安全模块。现在不用担心这个问题,因为我们有一个专门的章节介绍 SELinux。

保持系统更新;Linux 供应商提供更新是有原因的。可以手动进行更新,也可以使用自动化/编排工具。一定要做!

技术要求

在本章中,您需要部署 RedHat/CentOS 7 和 Ubuntu 18.04 VM。另一个选择是使用 SUSE SLE 12 或 openSUSE LEAP 而不是 CentOS 和 Ubuntu VM。SUSE 支持本章讨论的所有选项。

DAC

DAC 也被称为用户控制的访问控制。您可能已经熟悉 Linux 中的经典权限和 ACL。这两者结合形成了 DAC。经典权限检查当前进程的 用户 IDUID)和 组 IDGID)。经典权限将试图访问文件的用户的 UID 和 GID 与文件设置的 UID 和 GID 进行匹配。让我们看看 DAC 是如何引入的,以及您在 Linux 中拥有什么级别的权限。但是,我们不会详细讨论这个问题,因为主要目的是让您熟悉 Linux 中的权限。

DAC 简介

大多数操作系统,如 Linux、macOS、各种 Unix 的变种,甚至 Windows,都是基于 DAC 的。MAC 和 DAC 在美国国防部发布的《可信计算机系统评估标准》(TCSEC),也称为橙皮书中定义。我们将在下一节讨论 MAC。顾名思义,DAC 允许文件的所有者或创建者决定他们需要为同一文件提供其他用户的访问级别。

尽管我们看到 DAC 在所有系统中都得到了实施,但它也被认为是薄弱的。例如,如果我们授予用户读取权限,它将具有传递性质。因此,没有任何东西会阻止用户将别人文件的内容复制到用户可以访问的对象中。换句话说,信息的分发在 DAC 中没有得到管理。在下一节中,我们将快速了解文件权限。

Linux 中的文件权限

Linux 中的每个文件和目录都被视为一个对象,并且具有三种所有者类型:用户、组和其他。接下来,我们通常将文件和目录称为对象。首先,让我们了解三种不同类型的所有者:

  • 用户:用户是创建对象的人。默认情况下,这个人将成为对象的所有者。

  • :组是用户的集合。所有属于同一组的用户将对对象具有相同的访问级别。组的概念使您能够一次为多个用户分配权限更加容易。想象一种情况,您将创建一个文件,并且希望您的团队成员也能访问该文件。如果您是一个庞大的团队,并且为每个用户分配权限,这将是繁琐的。相反,您可以将用户添加到一个组中,并为该组定义权限,这意味着组中的所有用户都继承了访问权限。

  • 其他:这指的是任何不是对象所有者(创建者)或不是用户组成员的其他用户。换句话说,想象一个包含创建者和具有权限的组中的所有用户的集合;“其他”指的是不是这个集合元素的用户。

如前所述,每个对象都有三种所有者类型。每个所有者(用户、组、所有者)对对象都有三种权限。它们如下:

  • 读取:读取权限将允许读取或打开文件。目录上的读取权限意味着用户将能够列出目录的内容。

  • 写入:如果应用于文件,这将允许修改文件的内容。将此权限添加到目录将授予添加、删除和重命名文件的权限。

  • 执行:这个权限对于运行可执行程序或脚本是必需的。例如,如果你有一个 bash 脚本并且有读/写权限,这意味着你可以读取和修改代码。然而,要执行代码,你需要这个权限。

这是所有者和相关权限的图示表示:

代表三种所有者(用户、组和其他人)及其在对象上的读、写和执行权限的流程图

图 6.1:所有者类型和访问权限

让我们继续了解如何从 Linux 终端中找出权限。

要列出目录的内容,执行ls -lah

输出将根据你要列出的目录中的内容而有所不同:

列出目录内容的输出

图 6.2:列出目录的内容

如果观察数据行,第一个字母是d,这意味着它是一个目录。至于external.png,显示的是-,代表一个文件,而homel,意味着一个链接(更像是一个快捷方式)。

让我们仔细看一下:

目录输出数据行的一瞥

图 6.3:目录输出的数据行

首先,rwx表示用户/所有者有读、写和执行权限。

第二,r-x表示组有读和执行权限。然而,没有写权限。

第三,r-x表示所有其他人都有读和执行权限,但没有写权限。

同样地,你可以理解分配给其他对象的权限。

这些将按顺序写成读(r)写(w)执行。如果有一个字母缺失,那意味着该权限不存在。下面是一个解释这些字母代表的表格:

访问权限列表及其对应的符号

图 6.4:访问权限的符号

你可能想知道这个文件的所有者是谁,以及哪个组在访问。这在输出中已经有答案了:

包含有关所有者和组的信息的输出

图 6.5:所有者和组的详细信息

在这种情况下:

  • 用户有读和写权限,但没有执行权限。

  • 组只有读权限,没有写和执行权限。

  • 所有其他人只有读权限。

以下的图表将帮助你理解如何区分每个所有者的权限:

理解不同所有者的各种权限之间的差异

图 6.6:区分每个所有者的权限

你可以使用chmod命令来改变文件或文件夹的权限。一般的语法是:

chmod permissions filename/directory

然而,对目录应用权限并不会继承到子文件夹和文件中。如果希望权限被继承,可以使用-R参数,代表递归

此命令也不会给出任何输出;也就是说,无论是否应用了权限,它都不会返回任何输出。你可以使用-v参数来获得详细输出。

有两种方式可以传递权限给chmod命令:

  • 符号方法

  • 绝对方法/数字模型

符号方法

在符号方法中,我们将使用操作符和用户表示。以下是操作符列表:

用于设置、添加和移除权限的操作符列表

图 6.7:符号方法中的操作符

以下是用户表示列表:

用户表示列表

图 6.8:用户表示

现在,让我们看看如何结合操作符和表示法来更改权限。我们将使用-v参数来理解发生了什么变化。

让我们回顾一下我们对external.png文件的权限:

查看 external.png 文件的权限

图 6.9:external.png 文件的权限

目前,用户没有执行权限。要添加这些权限,请执行以下命令:

chmod -v u+x external.png

在输出中,您可以看到该值从rw-r--r--更改为rwxr--r--

为用户添加执行权限

图 6.10:添加执行权限

这里会显示一些数字。我们将在讨论绝对方法时讨论这些内容。

接下来,让我们尝试为组添加写和执行权限,执行以下命令:

chmod -v g+wx external.png

因此,向g(组)添加wx(写、执行)将给您一个类似以下的输出。您可以清楚地从输出中理解变化:

为组添加写和执行权限

图 6.11:为组添加写和执行权限

到目前为止,我们一直在添加权限。现在,让我们看看如何移除其他人的现有读权限。

执行以下命令:

chmod -v o-r external.png

这将移除读权限,从以下输出中可以明显看出:

显示读权限更改的输出

图 6.12:移除读权限

让我们为所有人(用户、组和其他人)设置读、写和执行权限。

执行以下命令:

chmod -v a=rwx external.png

输出显示权限更改为rwxrwxrwx

为所有所有者(用户、组和其他人)设置读、写和执行权限

图 6.13:为所有人设置读、写和执行权限

另一个例子涉及将每个所有者的权限组合在一起,并一次性传递这些权限,如下所示:

chmod -v u=rw,g=r,o=x external.png

在这里,用户权限设置为读写,组权限设置为只读,其他权限设置为仅执行。同样,您可以使用逗号分隔权限,并使用必要的运算符授予权限。

绝对(数字)节点

在这种方法中,我们将使用一个三位八进制数来设置权限。以下是数值及其对应权限的表格:

数字值及其对应权限的列表

图 6.14:数字值及其对应权限

让我们举个例子。检查位于当前目录中的new-file文件的权限。执行ls -lah

执行 ls -lah 以检查 new-file 的权限

图 6.15:检查 new-file 的权限

现在,让我们使用数字模式并分配权限。我们将把用户权限更改为rwx,因此 4 + 2 + 1 = 7,然后将组权限更改为rw,因此 4 + 2 + 0 = 6,其他人仅执行,因此 0 + 0 + 1 = 1。

将这三个数字组合起来,我们得到 761,因此这是我们需要传递给chmod的值。

执行以下命令:

chmod -v 761 new-file

输出如下:

使用三位八进制代码分配权限

图 6.16:使用三位八进制代码分配权限

现在,我们可以将我们在使用符号方法进行测试时得到的数字与之前的输出相关联。

这是该值的图示表示:

三位八进制代码的图示表示

图 6.17:三位八进制代码的图示表示

您可能已经注意到我们分配的权限之前有一个额外的数字(例如,0761)。这个0是用于高级文件权限。如果您还记得提示,我们有“这些目录中文件的 suid 和 sgid 权限非常危险”和“尽量避免使用 suid/sgid 位”。这些suid/sgid值通过额外的数字传递。最好不要使用这个,而是坚持使用基本权限,因为这些非常危险且复杂。

现在我们知道如何更改权限,但是我们如何更改拥有用户和组呢?为此,我们将使用chown命令。语法如下:

chown user:group filename/directory

这将更改文件的所有者和组。如果只想更改所有者,可以使用这个:

chown user filename/directory

如果只想更改组,使用chgrp命令:

chgrp group filename/directory

就像在chown命令的情况下解释的那样,这个命令也不是递归的。如果要使更改继承到目录的子文件夹和文件中,使用-R(递归)参数。就像我们在chmod中看到的那样,您还有一个详细的(-v)选项。

现在我们知道如何处理权限,让我们进入下一个关于 MAC 的部分。DAC 完全是关于使用 UID 和 GID 进行权限检查。另一方面,MAC 是基于策略的访问控制。现在让我们更仔细地看看 MAC。

MAC

在 MAC 中,系统根据特定资源的授权和敏感性限制对特定资源的访问。它更多地基于策略,并使用Linux 安全模块LSM)来实现。

安全标签是 MAC 的核心。每个主体都被赋予了一定级别的安全许可(例如,机密或保密),每个数据对象都有安全分类。例如,一个安全许可级别为保密的用户试图检索一个具有绝密安全分类的数据对象,因为他们的许可级别低于对象的分类,所以被拒绝访问。

因此,很明显,您可以在机密性非常重要的环境中(政府机构等)大多数情况下使用 MAC 模型。

SELinux 和 AppArmor 是基于 MAC 的商业系统的例子。

LSM

LSM 是一个提供在 DAC 之上添加 MAC 接口的框架。这种额外的安全层可以通过 SELinux(基于 Red Hat 的发行版和 SUSE)、AppArmor(Ubuntu 和 SUSE)或较少人知道的 Tomoyo(SUSE)来添加。在本节中,我们将介绍 SELinux 和 AppArmor。

DAC 是一个基于用户组成员和文件设备权限的访问控制模型。MAC 限制对资源对象的访问,例如以下内容:

  • 文件

  • 进程

  • TCP/UDP 端口

  • 用户及其角色

MAC,由 SELinux 实现,通过为每个资源对象分配一个分类标签,也称为上下文标签,来工作,而 AppArmor 是基于路径的。在任何情况下,如果一个资源对象需要访问另一个对象,它需要许可。因此,即使黑客成功进入您的网络应用程序,其他资源仍然受到保护!

SELinux

正如我们之前提到的,SELinux 是 Linux 中的一个安全模块,作为一个安全提示,建议不要禁用它。SELinux 是由 NSA 和 Red Hat 开发的。最初发布于 2000 年 12 月 22 日,在撰写本书时,可用的稳定版本是 2019 年发布的 2.9 版。它可以在每个基于 Red Hat 的发行版和 SUSE 上使用。本书将介绍在 Red Hat 上的实现。如果您想在 SUSE 上使用它,请访问 SUSE 文档doc.opensuse.org/documentation/leap/security/html/book.security/cha-selinux.html来安装和启用 SELinux。之后,程序是相同的。过去曾经努力使其在 Ubuntu 上运行,但目前没有积极的开发,而且软件包已经损坏。

所有访问必须明确授权,但在使用 SELinux 的发行版上,已经有许多策略。这几乎涵盖了每个资源对象。除了文档中已经提到的列表之外,还包括以下内容:

  • 完整的网络堆栈,包括 IPsec

  • 内核功能

  • 进程间通信IPC

  • 内存保护

  • 文件描述符(通信通道)继承和传输

对于 Docker 等容器虚拟化解决方案,SELinux 可以保护主机并在容器之间提供保护。

SELinux 配置

SELinux 是通过/etc/selinux/config文件进行配置的:

#  This file controls the state of SELinux on the system.
#  SELINUX= can take one of these three values:
#  enforcing - SELinux security policy is enforced.
#  permissive - SELinux prints warnings instead of enforcing.
#  disabled - No SELinux policy is loaded.
SELINUX=enforcing

在生产环境中,状态应该是enforcing模式。策略是强制执行的,如果访问受限,可以进行审计以修复 SELinux 引起的问题。如果您是软件开发人员或打包人员,并且需要为您的软件创建 SELinux 策略,permissive模式可能会有用。

可以使用setenforce命令在enforcingpermissive模式之间切换。使用setenforce 0切换到 permissive 模式,使用setenforce 1切换回 enforcing 模式。getenforce命令可用于查看当前状态:

#  SELINUXTYPE= can take one of these three values:
#  targeted - Targeted processes are protected,
#  minimum - Modification of targeted policy.
#  Only selected processes are protected.
#  mls - Multi Level Security protection.
SELINUXTYPE=targeted

默认策略—targeted,保护所有资源,并为大多数工作负载提供足够的保护。多级安全MLS)通过使用类别和敏感性提供的许可级别以及 SELinux 用户和角色提供额外的安全性。这对于提供文件共享的文件服务器非常有用。

如果选择了minimum类型,那么只有最基本的保护;如果需要更多的保护,就需要自己配置其他所有内容。如果在保护多进程应用程序(通常是非常老的应用程序)时遇到困难,并且生成的策略去除了太多限制,那么这种类型可能会有用。在这种情况下,最好是让特定应用程序不受保护,并保护系统的其余部分。在本节中,我只会讨论SELINUXTYPE=targeted,这是最常用的选项。

要显示 SELinux 的状态,可以使用sestatus命令。输出应该类似于以下屏幕截图:

运行 sestatus 命令查看 SELinux 状态

图 6.18:SELinux 状态

在探索 SELinux 之前,您需要向系统添加必要的软件包,以便审计 SELinux。请执行以下命令:

sudo yum install setroubleshoot

之后,您需要重新启动虚拟机:

sudo systemctl reboot

重启后,我们准备使用和排除 SELinux 故障:

SELinux context on ports

让我们从涉及 SSH 服务的简单示例开始。如前所述,所有进程都带有上下文标签。为了使此标签可见,许多实用程序,如lspslsof,都有-Z参数。首先,您需要找到此服务的主要进程 ID:

systemctl status sshd | grep PID

使用此进程 ID,我们可以请求上下文标签:

ps -q <PID> -Z

上下文标签是system_usystem_rsshd_ts0-s0,c0.c1023。因为我们使用的是有针对性的 SELinux 类型,所以我们只关心 SELinux 类型部分:sshd_t

SSH 正在端口 22 上运行。现在让我们调查端口的标签:

ss -ltn sport eq 22 -Z

您将确定上下文标签是system_usystem_rsshd_ts0-s0c0.c1023,换句话说,完全相同。不难理解sshd进程确实具有以相同标签运行在此端口的权限:

sshd 进程的上下文标签

图 6.19:sshd 进程的上下文标签

这并不总是那么简单,但在进入更复杂的情景之前,让我们将 SSH 服务器监听的端口修改为端口 44。要这样做,请编辑/etc/ssh/sshd_config文件:

sed -i 's/#Port 22/Port 44/' /etc/ssh/sshd_config

然后,重新启动 SSH 服务器:

sudo systemctl restart sshd

这将失败:

Job for sshd.service failed because the control process exited with error code.
See systemctl status sshd.service and journalctl -xe for details.

如果您执行journalctl -xe命令,您将看到以下消息:

SELinux is preventing /usr/sbin/sshd from name_bind access  
on the tcp_socket port 44.

有多种方法可以用于排除 SELinux 故障。您可以直接使用日志文件/var/log/audit/audit.log,或者使用sealert -a /var/log/audit/audit.log命令,或者使用journalctl命令:

journalctl --identifier setroubleshoot

日志条目还说明了以下内容:

For complete SELinux messages run: sealert -l <audit id>

执行此命令(可能将输出重定向到文件或通过lessmore进行管道传输),它不仅会再次显示相同的 SELinux 消息,而且还会提出如何修复它的建议:

If you want to allow /usr/sbin/sshd to bind to network port 44
Then you need to modify the port type.
Do 
# semanage port -a -t PORT_TYPE -p tcp 44 
where PORT_TYPE is one of the following: ssh_port_t, vnc_port_t, xserver_port_t.

在进入此解决方案之前,SELinux 与包含资源对象和上下文标签的多个数据库一起工作,即应将/应用于资源对象。semanage工具可用于修改数据库并向其中添加条目;在我们的情况下,是端口数据库。日志的输出建议为 TCP 端口 44 添加上下文标签到数据库。有三种可能的上下文;它们都将解决您的问题。

另一个重要的方面是有时还有其他可能的解决方案。有一个信心评级可以让您更容易地做出选择。但即使如此,您仍然必须仔细阅读。特别是对于文件,有时,您希望添加一个正则表达式,而不是一遍又一遍地为每个文件做同样的事情。

您可以采取一种实用的方法并声明“我不使用vncxserver,所以我选择ssh_port_t”,或者您可以使用sepolicy实用程序,该程序是policycoreutils-devel软件包的一部分。如果您收到错误消息,请使用sudo yum install –y policycoreutils-devel安装policycoreutils-devel

sepolicy network -a /usr/sbin/sshd 

在输出中搜索 TCP name_bind,因为 SELinux 访问正在阻止/usr/sbin/sshdtcp_socket port 44进行name_bind访问。

现在您知道建议来自何处,请查看端口 22 的当前标签:

sepolicy network -p 22 

标签是ssh_port_t

注意

您可以使用semanage port -lgrep来查找端口 22 的内容。

使用相同的标签确实是有道理的。不相信?让我们生成手册页:

sepolicy manpage -a -p /usr/share/man/man8/ 
mandb  

ssh_selinux手册页告诉您ssh_port_t

最后,让我们解决问题:

semanage port -a -t ssh_port_t -p tcp 44 

您不必重新启动sshd服务;systemd将在 42 秒内自动重新启动此服务。顺便说一句,sshd_config文件已经有一条注释描述了这个修复。在#Port 22之前的行中明确说明了这一点:

If you want to change the port on a SELinux system, you have to tell:
# SELinux about this change. 
# semanage port -a -t ssh_port_t -p tcp #PORTNUMBER 

最好撤消配置更改并将其重新配置为端口 22;否则,您可能会被锁在测试系统外。

文件上的 SELinux 上下文

在我们与 SELinux 的第一次会议以及调查端口上的上下文标签之后,现在是时候调查文件上的上下文标签了。作为示例,我们将使用vsftpd和 FTP 客户端:

sudo yum install vsftpd ftp 

然后,创建一个名为/srv/ftp/pub的目录:

sudo mkdir -p /srv/ftp/pub 
chown -R ftp:ftp /srv/ftp 

然后在/srv/ftp中创建一个文件:

echo WELCOME > /srv/ftp/README 

编辑配置文件/etc/vsftpd/vsftpd.conf,并在local_enable=YES行下添加以下内容:

anon_root=/srv/ftp 

这将使/srv/ftp成为匿名用户vsftpd服务的默认根目录。现在您可以开始服务了:

sudo systemctl start vsftpd.service
sudo systemctl status vsftpd.service

使用ftp实用程序,尝试以用户anonymous的身份登录到 FTP 服务器,无需密码:

ftp localhost 

Trying ::1... 
Connected to localhost (::1). 
220 (vsFTPd 3.0.2) 
Name (localhost:root): anonymous 
331 Please specify the password. 
Password: 
230 Login successful. 
Remote system type is UNIX. 
Using binary mode to transfer files. 
ftp> ls 
229 Entering Extended Passive Mode (|||57280|). 
150 Here comes the directory listing. 
-rw-r--r-- 1 14 50 8 Jul 16 09:47 README 
drwxr-xr-x 2 14 50 6 Jul 16 09:44 pub 
226 Directory send OK. 
Try to get the file:  
get README 

而且它有效!为什么会这样?因为数据库中已经有了/srv/ftp/README的正确标签条目:

semanage fcontext -l | grep /srv  

上面的命令显示以下行:

/srv/([^/]*/)?ftp(/.*)? all files system_u:object_r:public_content_t:s0 

在创建新文件时应用:

stat -c %C /srv/ftp/README 
ls -Z /srv/ftp/README 

这两个命令告诉你类型是public_content_tftpd_selinux的 man 页面有两个在这里很重要的部分:public_content_t类型只允许你读取(下载)文件,但不允许你使用这种类型写入(上传)文件。你需要另一种类型public_content_rw_t才能上传文件。

创建一个上传目录:

mkdir -m 2770 /srv/ftp/incoming 

chown -R ftp:ftp /srv/ftp/incoming 

查看当前标签并更改它:

ls -dZ /srv/ftp/incoming 

semanage fcontext -a -t public_content_rw_t "/srv/ftp/incoming(/.*)?" 

restorecon -rv /srv/ftp/incoming 

ls -dZ /srv/ftp/incoming 

首先,你必须将策略添加到fcontext数据库;之后,你可以将策略应用到已经存在的目录。

注意

阅读selinux-fcontext的 man 页面。除了描述所有选项外,还有一些很好的例子。

SELinux 布尔值

使用单个字符串,你可以改变 SELinux 的行为。这个字符串被称为SELinux 布尔值。你可以使用getsebool -a获取布尔值及其值的列表。使用boolean allow_ftpd_anon_write,我们将改变 SELinux 的反应方式。再次匿名连接到 FTP 服务器并尝试上传文件:

ftp> cd /incoming 
250 Directory successfully changed. 
ftp> put /etc/hosts hosts 
local: /etc/hosts remote: hosts 
229 Entering Extended Passive Mode (|||12830|). 
550 Permission denied. 

journalctl --identifier setroubleshoot命令非常清楚地告诉你:

SELinux is preventing vsftpd from write access on the directory ftp.   

sealert命令将为你提供修复问题所需的信息:

setsebool -P allow_ftpd_anon_write 1 

那么,这里发生了什么?有时,对于端口或文件的简单规则是不够的,例如,如果一个 NFS 共享也必须与 Samba 一起导出。在这种情况下,你可以创建自己的复杂 SELinux 策略,或者使用易于使用的开关数据库。为此,你可以使用较旧的setsebool实用程序或semanage

semanage boolean --list | grep "ftpd_anon_write" 
semanage boolean --modify ftpd_anon_write --on 

使用setsebool而不加上-P会进行更改,但不是持久的。semanage实用程序没有选项可以进行非永久更改。

AppArmor

在 Debian、Ubuntu 和 SUSE 发行版中,AppArmor 可用于实现 MAC。请注意,各发行版之间存在一些细微差异,但总的来说,一个发行版可以添加更少或更多的配置文件和一些额外的工具。在本节中,我们以 Ubuntu 18.04 为例。

此外,你必须确保保持你的发行版最新,特别是 AppArmor;Debian 和 Ubuntu 的软件包经常受到错误的困扰,有时会导致意外的行为。

确保必要的软件包已安装:

sudo apt install apparmor-utils apparmor-easyprof \ 
  apparmor-profiles apparmor-profiles-extra apparmor-easyprof 

与 SELinux 相比,存在一些基本差异:

  • 默认情况下,只有最低限度受到保护。你必须为每个应用程序应用安全性。

  • 你可以混合强制和投诉模式;你可以针对每个应用程序做出决定。

  • 当 AppArmor 开发开始时,范围相当有限:进程和文件。如今,除了进程和文件,你还可以用它来进行基于角色的访问控制(RBAC)、MLS、登录策略以及其他方面的控制。

在本章中,我们将涵盖初始范围:需要访问文件的进程。

AppArmor 状态

首先要做的是检查 AppArmor 服务是否正在运行:

sudo systemctl status apparmor 

或者,执行以下命令:

sudo aa-enabled 

之后,使用以下命令更详细地查看状态:

sudo apparmor_status 

以下是上述命令的替代方法:

sudo aa-status 

以下截图显示了使用apparmor_status命令派生的 AppArmor 状态:

使用apparmor_status命令检查 AppArmor 状态

图 6.20:AppArmor 状态

生成 AppArmor 配置文件

你想要保护的每个应用程序都需要一个配置文件,可以由apparmor-profilesapparmor-profiles-extra软件包、应用程序软件包或你自己提供。这些配置文件存储在/etc/apparmor.d中。

让我们以安装 nginx web 服务器为例:

sudo apt install nginx 

如果您浏览/etc/apparmor.d目录,您会发现没有 nginx 的配置文件。创建一个默认的:

sudo aa-autodep nginx 

创建了一个配置文件:/etc/apparmor.d/usr.sbin.nginx。这个文件几乎是空的,只包括一些基本规则和变量,称为抽象,以及以下行:

/usr/sbin/nginx mr, 

mr值定义了访问模式:r表示读取模式,m允许将文件映射到内存中。

让我们强制执行 nginx 的模式:

sudo aa-enforce /usr/sbin/nginx 
sudo systemctl restart nginx 

nginx 将无法启动。前述命令的输出如下:

sudo journalctl --identifier audit 

这非常明显地指向了 AppArmor:

sudo journalctl -k | grep audit 

要解决问题,请为此配置文件设置投诉模式。这样,它不会强制执行策略,但会对安全策略的每个违规行为进行投诉:

sudo aa-complain /usr/sbin/nginx 
sudo systemctl start nginx 

通过浏览器或实用程序(例如curl)发出http请求:

curl http://127.0.0.1 

下一步是扫描“日志文件”并批准或拒绝每个操作:

sudo aa-logprof 

非常仔细地阅读并使用箭头键选择正确的选项(如果需要):

配置 nginx 配置文件

图 6.21:配置 nginx 配置文件

LXCLinux 容器)是一种容器技术,我们只是在为 web 服务器配置配置文件。似乎修复 DAC 的问题是一个不错的选择:

使用 DAC 配置 web 服务器的配置文件

图 6.22:修复 nginx 的 DAC

审计建议一个新模式:w表示对/var/log/nginx/error.log文件的写访问。

此外,您可以阻止访问以下目录:

  • /etc/ssl/openssl.conf的读访问。这是一个困难的问题,但是ssl的抽象听起来是正确的。

  • /etc/nginx/nginx.conf的读访问。同样,不是一个容器,因此文件的所有者必须是 OK 的。

  • 一般来说,文件的所有者是一个不错的选择。

现在,是时候保存更改并重试了:

sudo aa-enforce /usr/sbin/nginx 
sudo systemctl restart nginx
curl http://127.0.0.1 

一切似乎现在都运行正常,至少对于对一个简单网站的请求。正如您所看到的,这主要是基于合理的猜测。另一种选择是深入研究所有建议的抽象。

创建的文件/etc/apparmor.d/usr.sbin.nginx相对容易阅读。它以应该对每个配置文件可用的所有可调整变量开始:

#include <tunables/global> 

文件之后是其他抽象,比如以下内容:

#include <abstractions/nameservice 

要知道他们在做什么,只需查看文件。例如,/etc/apparmor.d/abstractions/nameservice文件中列出了以下内容:

/usr/sbin/nginx flags=(complain) { 
 #include <abstractions/base> 
 #include <abstractions/nameservice> 
 #include <abstractions/openssl> 
 #include <abstractions/web-data> 

注意

许多程序希望执行类似名称服务的操作,例如按名称或 ID 查找用户、按名称或 ID 查找组,以及按名称或 IP 查找主机。这些操作可以通过 DNS、NIS、NIS+、LDAP、hesiod 和 wins 文件执行。在这里允许所有这些选项。

下一节是关于 Posix 功能的;有关更多信息,请参阅man 7 capabilities

capability dac_override, 

最后一节是权限;有关完整列表,请参阅man 5 apparmor.d

/var/log/nginx/error.log w, 
 owner /etc/nginx/modules-enabled/ r, 
 owner /etc/nginx/nginx.conf r, 
 owner /run/nginx.pid w, 
 owner /usr/lib/nginx/modules/ngx_http_geoip_module.so mr, 
 owner /usr/share/nginx/modules-available/mod-http-geoip.conf r, 
 owner /usr/share/nginx/modules-available/mod-http-image-filter.conf r, 
 owner /var/log/nginx/access.log w, 
} 

特别是在开始使用aa-logprof时,可能会有点压倒性。但是配置文件并不难阅读;每个选项都在两个 man 页面中,并且包含的抽象都有注释进行了记录。

firewalld 和 systemd

第五章高级 Linux 管理中,systemd 被介绍为系统和服务管理器。在 systemd 中,有几个选项可以为您的守护进程和文件系统添加额外的保护层。

老实说,我们认为在 Azure 网络安全组之上使用 Azure 防火墙确实是有道理的。它易于设置,提供集中管理,并且几乎不需要维护。它在 VM、虚拟网络甚至不同的 Azure 订阅之间提供安全性。

注意

如果您想使用这个防火墙,还会有额外的费用。但是 Linux 防火墙不会产生任何费用,因为它是安装在您的机器上的安全措施。

在 Azure 防火墙和 Linux 防火墙之间的选择取决于许多因素:

  • 成本

  • VM 和应用程序的部署和编排

  • 不同的角色:是否有一个管理员负责一切?

希望在介绍了 Linux 防火墙实现之一后,可以清楚地了解 Linux 防火墙绝不是 Azure 防火墙的完全替代品。它只能为 VM 的传入流量提供安全性,是的,也可以配置此防火墙来阻止传出流量,但这相当复杂。另一方面,如果它配置在 Azure 网络安全组之上,在许多情况下,这已经足够了。

Linux 有不同类型的防火墙解决方案,包括 firewalld 和 iptables。在本书中,我们将遵循 firewalld,因为它具有可用的配置选项和流行度。请确保已安装 firewalld 软件,并且已从系统中删除其他防火墙软件,以避免冲突。在基于 RHEL/CentOS 的发行版中,这已经是这样。在 Ubuntu 中,使用以下命令:

sudo apt remove ufw 
sudo apt install firewalld 

在基于 SUSE 的发行版中,使用以下命令:

sudo zypper install susefirewall2-to-firewalld 
sudo susefirewall2-to-firewalld -c 

Linux 有多种防火墙实现;其中一些甚至是为特定发行版开发的,比如 SuSEfirewall2。在本章中,我们将介绍 firewalld,它在每个发行版上都可用。

firewalld 由一个管理防火墙所有组件的守护程序组成:

  • 区域

  • 接口

  • 来源

  • iptables 和 ebtables 的直接规则(本书未涉及)

firewalld 利用内核模块:iptables/IP6 表用于 IPv4 和 IPv6 流量,ebtables 用于过滤通过 Linux 桥接的网络流量。在较新的发行版中,比如 RHEL 8,使用 nftables 模块。

要配置 firewalld 规则,有一个命令行实用程序可用:firewall-cmd。规则可以是运行时的或持久的。这种行为有两个重要的原因:这样就不需要重新加载所有规则,意味着临时的安全风险。您可以动态添加和删除规则。如果您犯了一个错误,因此无法再次登录,只需重新启动作为一个快速解决方案。我们还可以使用 systemd-run --oncalendar 命令创建一个定时任务,执行 firewall-cmd --reload,这是一个更好的解决方案:

sudo systemd-run --on-calendar='2018-08-20 13:00:00' \ 
  firewall-cmd --reload  

sudo systemctl list-timers 

如果防火墙规则正确(并且您没有被锁定),不要忘记停止和禁用计时器。

您还可以使用编排工具配置守护程序,这些工具与守护程序通信或将 XML 文件推送到主机。

注意

端口仅对连接到虚拟机网络的虚拟机开放,除非您在网络安全组中打开了端口!

重要的是要知道 Azure Service Fabric(基础设施)将根据需要向防火墙配置添加额外规则。建议不要删除这些规则,因为它们很重要,因为它们被 Azure 平台使用。如果您使用 journalctl 命令在日志数据库中搜索,就可以看到这一点:

sudo journalctl | grep "Azure fabric firewall"

使用 iptables-save 命令查看所有活动防火墙规则,或者如果您的发行版使用 nftables

sudo nft list ruleset

firewalld 区域

firewalld 的最重要的概念之一是区域。区域包括默认规则(称为目标)、网络接口或网络源,以及其他服务、端口、协议和丰富规则。

只有在网络接口连接到接口或网络源时,区域才处于活动状态。

要列出可用的区域,请使用以下命令:

sudo firewall-cmd --get-zones

这些区域配置在 /usr/lib/firewalld/zones 中。您不应该对这些文件进行更改。新区域或对区域的更改将写入 /etc/firewalld/zones 目录。

默认区域是公共区域:

sudo firewall-cmd --get-default-zone

要列出公共区域的区域配置,请使用以下命令:

sudo firewall-cmd --zone public --list-all

区域配置如下所示:

public 
  target: default 
  icmp-block-inversion: no 
  interfaces: 
  sources: 
  services: ssh dhcpv6-client 
  ports: 
  protocols: 
  masquerade: no 
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 

公共区域的目标策略是 default,这意味着默认情况下会阻止所有传入的东西,除非配置了服务、端口和协议。

该区域的相应 /usr/lib/firewalld/zones/public.xml 文件如下:

<?xml version="1.0" encoding="utf-8"?> 
<zone> 
 <short>Public</short> 
 <description>For use in public areas. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description> 
 <service name="ssh"/> 
 <service name="dhcpv6-client"/> 
</zone> 

还有用于配置伪装和端口转发的选项。丰富规则是高级防火墙规则,如firewalld.richlanguage手册中所述。

执行man firewalld.richlanguages,如下截图所示:

命令的输出

图 6.23:man firewalld.richlanguages命令的输出

根据您使用的发行版,可能会有其他服务名称。例如,如果您使用的是 RHEL 8,您可能会看到cockpit列为服务。cockpit是用于管理 RHEL 机器的基于 Web 的界面。

您可能已经注意到,在公共区域中,它说target: default。目标是默认行为。可能的值如下:

  • 默认:不执行任何操作,接受每个 ICMP 数据包,并拒绝其他所有内容。

  • %%REJECT%%:这通过 ICMP 协议向客户端发送拒绝响应。

  • DROP:这会发送 TCP SYN/ACK,就像在打开端口上一样,但所有其他流量都会被丢弃。没有 ICMP 消息通知客户端。

  • 接受:接受一切。

在 Ubuntu 中,默认情况下,没有附加网络接口。请不要在附加接口之前重新启动虚拟机!执行以下命令:

sudo firewall-cmd --add-interface=eth0 --zone=public 

sudo firewall-cmd --add-interface=eth0 --zone=public --permanent 

sudo firewall-cmd --zone=public --list-all 

如果修改区域,则文件将从/usr/lib/firewalld/zones复制到/etc/firewalld/zones。下一次修改将创建一个带有.old文件扩展名的区域备份,并创建一个包含修改内容的新文件。

firewalld 服务

服务是一个以应用为中心的配置,允许一个或多个端口。要接收可用服务的列表,请使用以下命令:

sudo firewall-cmd --get-services 

如果要添加服务,例如 MySQL,请执行以下命令:

sudo firewall-cmd --add-service=mysql --zone=public 

sudo firewall-cmd --add-service=mysql --zone=public \ 
  --permanent 

如果要从区域中删除服务,请使用--remove-service参数。

服务配置在/usr/lib/firewalld/services目录中。同样,您不应该修改这些文件。您可以通过将它们复制到/etc/firewalld/services目录中来更改它们或创建自己的服务。

也可以添加单个端口,但一般来说,这不是一个好主意:过一段时间后,您还能记得哪些端口被哪个应用程序使用吗?相反,如果服务尚未定义,请创建自己的服务。

现在,让我们为 Microsoft PPTP 防火墙协议创建一个服务文件,/etc/firewalld/services/pptp.xml

<?xml version="1.0" encoding="utf-8"?> 
<service> 
 <short>PPtP</short> 
 <description>Microsoft VPN</description> 
 <port protocol="tcp" port="1723"/> 
</service> 

在前面的文件中,您可以看到允许 TCP 端口1723。您可以添加尽可能多的端口规则。例如,如果要添加 TCP 端口1724,则行项目将如下所示:

<port protocol="tcp" port="1724" /> 

使用firewalld-cmd --reload重新加载防火墙后,服务可用。这还不够:GRE(通用路由封装)协议不被允许。要允许此协议,请使用以下命令:

sudo firewall-cmd --service=pptp --add-protocol=gre \ 
  --permanent 

sudo firewall-cmd --reload 

这将向服务文件添加以下行:

<protocol value="gre"/>  

您可以使用--remove-protocol参数删除协议。

firewalld 网络源

只有在网络接口连接到它或网络源时,区域才处于活动状态。将网络接口添加到拒绝区域是没有意义的。拒绝区域是所有传入数据包都被丢弃且不回复的地方;但是,允许传出连接。所以,正如我提到的,如果将网络接口添加到拒绝区域,所有传入数据包将被 firewalld 丢弃,这根本没有任何意义。

但是,添加网络源是有意义的。源由一个或多个条目组成:媒体访问控制地址、IP 地址或 IP 范围。

例如,出于任何原因,假设您想要阻止来自百慕大的所有流量。网站ipdeny.com可以为您提供 IP 地址列表:

cd /tmp 
wget http://www.ipdeny.com/ipblocks/data/countries/bm.zone 

有几种类型的ipset。要查看支持的ipset类型列表,请执行以下命令:

sudo firewall-cmd --get-ipset-types 

在我们的场景中,我们希望hash:net IP 范围的类型:

sudo firewall-cmd --new-ipset=block_bermuda --type=hash:net --permanent 
sudo firewall-cmd --reload 

现在,我们可以使用下载的文件向ipset添加条目:

sudo firewall-cmd --ipset=block_bermuda --add-entries-from-file=/tmp/bm.zone 
sudo firewall-cmd --ipset=block_bermuda --add-entries-from-file=/tmp/bm.zone \ 
  --permanent 
sudo firewall-cmd --reload 

最后一步涉及将ipset添加为区域的源:

sudo firewall-cmd --zone=drop --add-source=ipset:block_bermuda 
sudo firewall-cmd --zone=drop --add-source=ipset:block_bermuda --permanent 
sudo firewall-cmd --reload 

丢弃区的目的是在不让客户端知道流量被丢弃的情况下丢弃所有流量。将ipset添加到此区域会使其激活,并且来自百慕大的所有流量都将被丢弃:

sudo firewall-cmd --get-active-zones 
drop 
 sources: ipset:block_bermuda 
public 
 interfaces: eth0 

现在我们知道了 firewalld 的工作原理,以及如何使用区域来保护我们的机器,让我们跳到下一节。

systemd 安全

如前一章所述,systemd 负责在启动过程中并行启动所有进程,除了那些由内核创建的进程。之后,问题在于按需激活服务等。systemd 单元还可以提供额外的安全层。您可以向单元文件添加多个选项,以使您的单元更加安全。

只需使用systemctl edit编辑单元文件并添加安全措施。例如,执行以下命令:

sudo systemctl edit sshd 

然后,添加以下行:

[Service] 
ProtectHome=read-only 

保存文件,重新读取systemctl配置,并重新启动sshd

sudo systemctl daemon-reload 
sudo systemctl restart sshd 

现在,使用 SSH 客户端再次登录,并尝试在您的家目录中保存文件。这将失败,因为它是一个只读文件系统:

无法在家目录中保存文件,因为权限已更改为只读

图 6.24:登录失败,因为单元文件被更改为只读

限制对文件系统的访问

ProtectHome参数是一个非常有趣的参数。以下值可用:

  • true/home/root/run/user目录对该单元不可访问,并且对于在该单元内启动的进程来说是空的。

  • read-only:这些目录是只读的。

另一个非常相似的参数是ProtectSystem

  • true/usr/boot被挂载为只读。

  • full/etc被挂载为只读,以及/usr/boot

  • strict:整个文件系统是只读的,除了/proc/dev/sys

除了ProtectHomeProtectSystem之外,还可以使用以下参数:ReadWritePaths来列出目录,ReadOnlyPathsInaccessiblePaths

一些守护进程使用/tmp目录进行临时存储。这个目录的问题在于它是可读的。PrivateTmp=true参数为进程设置了一个新的临时文件系统,只能被该进程访问。

还有与内核相关的参数:ProtectKernelModules=true参数使加载模块变得不可能,ProtectKernelTunables=true参数使使用sysctl命令或手动在/proc/sys目录结构中更改内核参数变得不可能。

最后,SELinuxContextAppArmorProfile参数强制了单元的上下文。

限制网络访问

systemd 也可以用于限制网络访问,例如可以列出可以允许或拒绝的 IP 地址。在版本 235 之后的新版本 systemd,例如 Ubuntu 18.04、SLE 15 SP1 和 RHEL 8,还支持 IP 账户和访问列表来限制网络访问。

IPAccounting=yes允许一个单元收集和分析网络数据。要查看结果,可以使用systemctl命令:

systemctl show <service name> -p IPIngressBytes \ 
 -p IPIngressPackets \ 
 -p IPEgressBytes -p IPEgressPackets 

与每个参数一样,您也可以在systemd-run中使用它:

使用 systemd-run 和 systemctl 命令收集和分析网络数据

图 6.25:使用 systemd-run 和 systemctl 收集和分析网络数据

您还可以使用IPAddressDeny来拒绝 IP 地址或 IP 范围。可以使用IPAddressAllow进行例外。甚至可以在系统范围内拒绝所有内容,并在每个服务的基础上进行白名单处理:

sudo systemctl set-property sshd.service IPAddressAllow=any 
sudo systemctl set-property waagent.service IPAddressAllow=10.0.0.1 

注意

如果您使用的是 Ubuntu,服务名称是walinuxagent

sudo systemctl set-property system.slice IPAddressAllow=localhost  
sudo systemctl set-property system.slice IPAddressAllow=10.0.0.1  
sudo systemctl set-property system.slice IPAddressDeny=any  

更改保存在/etc/systemd/system.control目录结构中:

更改保存在 system.control 目录中

图 6.26:保存更改在 system.control 目录中

以下是一些备注:

  • 当然,您必须将 IP 范围更改为您的虚拟子网,并且必须允许对您的子网的第一个 IP 地址进行访问,以供 Azure 代理和网络服务使用,例如DHCP(动态主机配置协议)。

  • 将 SSH 访问限制为您自己网络的 IP 地址也是一个很好的主意。

  • 非常仔细地查看 systemd 日志,以找出是否需要打开更多端口。

systemd 访问列表功能可能不像 firewalld 那样先进,但它是应用级别限制的一个很好的替代方法(在守护程序的配置文件中使用 hosts allow 指令,或者对于使用 libwrap 支持编译的应用程序,使用/etc/hosts.allow/etc/hosts.deny)。在我们看来,在 Azure 中,您不需要更多。如果所有发行版都有最新版本的 systemd 就好了。

注意

我们不会在本书中涵盖libwrap库,因为越来越多的应用程序不再使用这个选项,一些供应商,如 SUSE,正忙于删除对这个库的完全支持。

Azure 中的身份和访问管理 - IAM

到目前为止,我们一直在讨论如何在 Linux 中管理安全性。由于我们在 Azure 中部署,Azure 还为我们的 Linux VM 提供了一些额外的安全性。例如,之前我们讨论了 Azure 防火墙和网络安全组,这有助于控制流量,限制对不需要的端口的访问,并过滤来自未知位置的流量。除此之外,Azure 还有其他服务,如 Azure AD 域服务,它将允许您将 Linux VM 加入到域中。最近,微软推出了一项选项,允许 Azure AD 用户登录 Linux VM。这样做的好处是您不必使用其他用户名;相反,您可以使用 Azure AD 凭据。让我们更仔细地了解这些服务,并了解如何利用它们来增加我们的 Linux VM 的安全性。

Azure AD 域服务

到目前为止,我们一直在讨论 Linux VM 内部可以做什么。由于我们在 Azure 上,我们应该利用Azure AD 域服务,通过它可以将 Linux 机器加入域并强制执行组织的策略。Azure AD 域服务是一个作为服务的域控制器,为您提供 DNS 服务和身份管理。集中身份管理始终是安全解决方案的重要组成部分。它使用户能够访问资源。除此之外,您还可以强制执行策略并启用多因素身份验证。

在这一部分,我们将重点讨论如何设置服务和加入域。

设置 Azure AD 域服务

设置 Azure AD 域服务的最简单方法是通过 Azure 门户。在左侧栏中,选择创建资源并搜索Domain Services。选择Azure AD 域服务,然后单击创建按钮。

在向导中,您将被要求进行一些设置:

  • .onmicrosoft.com。对于本书的目的,这就足够了。

  • 虚拟网络:创建一个新的虚拟网络和一个新的子网是一个好主意。标签并不重要。

  • AAD DC Administrators。要能够使用用户加入域,用户必须是该组的成员,在 Azure 门户中的左侧栏中使用Active Directory部分。

现在您已经准备好部署服务了。这将需要一些时间;根据我的个人经验,可能需要 20 到 30 分钟。

完成后,转到左侧栏中的虚拟网络部分,并输入新创建的虚拟网络。您将找到两个新创建的网络接口及其 IP 地址。您将需要这些信息,所以记下来。

在这个虚拟网络中创建一个新的子网是一个好主意,但不是必需的。

Linux 配置

您必须在与部署 Azure AD 目录服务的相同虚拟网络或对等网络中部署 Linux VM。正如所述,最好将其附加到另一个子网。在这里,我们不遵循安全 LDAP。

主机名

使用hostnamectl实用程序将主机名更改为正确的fqdn

sudo hostnamectl set-hostname ubuntu01.frederikvoslinvirt.onmicrosoft.com 

然后编辑/etc/hosts文件。添加以下条目:

127.0.0.1 ubuntu01.frederikvoslinvirt.onmicrosoft.com ubuntu01 

DNS 服务器

在 Azure 门户的左侧栏中,转到虚拟网络,并导航到 Azure AD 域服务网络接口所在的子网。选择DNS 服务器并使用自定义选项设置 Azure AD 域服务网络接口的 IP 地址。通过这样做,每当需要主机名的 DNS 解析时,它将指向 Azure AD 域服务。

或者,如果您的 Azure AD 域服务是新部署的,在 Azure 门户的概述窗格中,它将要求您更改 DNS 服务器。只需点击配置按钮即可将虚拟网络中的 DNS 服务器更改为指向 Azure AD 域服务。

通常,重新启动 VM 中的网络应该就足够了,但最好现在重启。有时,旧设置和新设置都会生效。

在 RHEL、Ubuntu 和 SUSE 中,查看/etc/resolv.conf文件的内容以验证结果。然后,查看eth0的设置。

安装依赖项

有一些重要的组件和依赖项是必需的,才能使用 Azure AD 域服务:

  • 用于授权的 Kerberos 客户端

  • SSSD,负责配置和利用功能,如使用和缓存凭据的后端

  • Samba 库,以兼容 Windows 功能/选项

  • 一些用于加入和管理域的实用程序,如realmadclinet命令

安装必要的软件以便能够加入域。

对于基于 RHEL/CentOS 的发行版,执行以下命令:

sudo yum install realmd sssd krb5-workstation krb5-libs samba-common-tools 

在 Ubuntu 中,执行以下命令:

sudo apt install krb5-user samba sssd sssd-tools libnss-sss libpam-sss realmd adcli 

在 SLE/OpenSUSE LEAP 中,依赖项将由 YaST 处理。

加入域 - Ubuntu 和 RHEL/CentOS

在 Ubuntu 和基于 RHEL/CentOS 的发行版中,realm实用程序可用于加入域。首先,发现域:

sudo realm discover <your domain> 

输出应类似于以下内容:

在 Ubuntu 和基于 RHEL/CentOS 的发行版中发现域

图 6.27:发现域

现在,您已经准备好加入域:

sudo realm join <your domain> -U <username@domain>

使用您之前添加的用户名作为 Azure AD 域服务管理员组的成员。如果收到未安装必要的软件包的消息,但您确定已安装,可以在realm命令中添加--install=/参数。

要验证结果,请执行以下命令:

sudo realm list

输出应类似于以下内容:

使用 realm 实用程序加入域

图 6.28:加入域

您应该能够执行以下操作:

id <user>@<domain>
su <user>@<domain>

使用此用户远程登录ssh

注意

如果这不起作用,并且加入成功,请重新启动 VM。

加入域 - SUSE

在 SUSE SLE 和 LEAP 中,加入域的最佳方法是使用 YaST。

启动 YaST 实用程序:

sudo yast

从 YaST 主窗口开始用户登录管理模块,然后点击更改设置。点击加入域并填写域名。之后,您将能够成功加入域。如果需要,将安装依赖项。

将出现一个新窗口来管理域用户登录。您至少需要以下内容:允许域用户登录创建主目录。在 Azure AD 域服务中,其他所有选项目前都不可能。

YaST 将为您在 shell 上提供一个类似 GUI 的彩色界面,使用它可以将机器加入域。运行sudo yast后,您将会得到如下所示的屏幕。从列表中,使用箭头键选择网络服务,然后选择Windows 域成员资格

运行 sudo yast 命令查看 YaST 界面

图 6.29:在 Shell 上的 YaST 界面

最好的部分是,如果缺少任何依赖项,YaST 将提示您安装它们,因此请继续并完成依赖项安装。安装完成后,您可以输入您的域名,一旦保存,您将被提示输入用户名和密码,如下面的屏幕截图所示:

在 YaST 中提供凭据以将机器注册到 Azure AD 域服务

图 6.30:提供注册机器的凭据

user@domain格式输入您的凭据,然后输入您的密码。完成流程后,SUSE 机器将连接到 Azure AD 域服务并注册您的机器。如果加入成功,您将在屏幕上收到一条消息,如下所示:

提示成功加入域的消息提示

图 6.31:域加入成功

您可以通过使用su命令将当前用户切换为您的 AD 用户名来进行验证,如下面的屏幕截图所示:

使用 su 命令验证域加入

图 6.32:验证域加入

最后,我们已经成功将我们的 Linux 机器加入了 Azure AD 域服务。最近,微软添加了对 Linux VM 进行 Azure AD 登录的支持,无需将机器加入域。将在下一节中讨论安装代理以完成授权。

使用 Azure AD 凭据登录到 Linux VM

Azure AD 还可以实现另一种形式的身份管理。这是一个完全不同的身份管理系统,没有 LDAP 和 Kerberos,正如前一节所讨论的。在 Linux 中,Azure AD 将允许您使用 Azure 凭据登录到您的 VM,但不支持应用程序级别。在撰写本书时,此功能仍处于预览阶段。此外,此功能在 SUSE 中不可用。

要使用 Azure AD,您必须部署一个 VM 扩展,例如使用 Azure CLI:

az vm extension set \ 
    --publisher Microsoft.Azure.ActiveDirectory.LinuxSSH \ 
    --name AADLoginForLinux \ 
    --resource-group myResourceGroup \ 
    --vm-name myVM 

之后,您必须为您的 Azure AD 帐户分配一个角色,可以是虚拟机管理员登录(具有 root 权限)或虚拟机用户登录(非特权用户)角色,并将范围限制在此 VM 上:

az role assignment create \ 
    --role "Virtual Machine Administrator Login" \ 
    --assignee <ad user name> \ 
    --scope <your vm> 

在这里,您可以在订阅级别设置范围,--scope /subscriptions/<subcription ID>。通过这样做,该角色将被订阅中的所有资源继承。

如果您只想对特定的 VM 进行细粒度访问控制,可以执行以下命令(在 PowerShell 中):

$vm = Get-AzVM –Name <VM Name> -ResourceGroup <resource group> 

$vm.Id 将为您提供虚拟机的范围。

在 bash 中,执行以下命令:

 az vm show --name<name> --resource-group <resource group> --query id 

此命令将查询虚拟机的 ID,并且是角色分配的范围。

您可以使用您的 AD 凭据登录:

ssh <ad user>@<ad domain>@<ip address> 

最后,您将能够看到您正在使用 Azure AD 凭据登录到 Linux VM。

Azure 中的其他安全解决方案

在本章中,我们已经讨论了如何提高 Linux 安全级别并整合某些 Azure 服务来提高安全性。话虽如此,可以用于提高安全性的 Azure 服务列表非常长。以下是其中一些重点:

  • Azure AD 托管身份:使用此功能,您可以为虚拟机创建托管身份,用于对支持 Azure AD 身份验证的任何服务进行身份验证。以前,此服务被称为托管服务身份MSI),现在称为Azure 资源的托管身份

  • 密钥保管库:可用于安全存储密钥。例如,在 Azure 磁盘加密中,密钥将存储在密钥保管库中,并在需要时进行访问。

  • Azure 磁盘加密:磁盘加密将帮助您加密操作系统磁盘以及数据磁盘,从而为存储的数据提供额外的安全性。

  • RBAC:Azure 中的 RBAC 允许您为虚拟机分配细粒度权限。Azure 中有许多内置角色可用,您可以根据安全需求分配其中一个。此外,您可以创建自定义 RBAC 角色以提供更细粒度的权限。

  • Azure 安全中心ASC):ASC 是一个统一的基础设施安全管理系统,旨在 consolida 您的安全。

  • Azure 策略客户端配置:这可用于审核 Linux 虚拟机内部的设置。在第八章 探索持续配置自动化中已经详细讨论过。

我们建议您阅读微软文档,以更好地了解这些服务如何在您的环境中使用,以加强整体安全性。

总结

安全是当今一个非常重要的话题。关于这个主题已经写了许多报告、书籍等。在本章中,我们介绍了 Linux 中增加安全级别的几种选项。所有这些选项都是在 Azure 通过网络安全组提供的基本安全性之上的。它们相对容易实施,并将产生重大影响!

中央身份管理不仅是为用户提供访问虚拟机的一种方式,也是减少安全风险的一部分。Azure AD 域服务通过 LDAP 和 Kerberos 为所有支持这些协议的操作系统和应用程序提供身份管理解决方案。

第八章,探索持续配置自动化,将涵盖自动化和编排。请注意,本章涵盖的所有安全措施都可以轻松进行编排。编排使得中央配置管理成为可能。其一个重要优势是防止错误和难以管理的配置。因此,即使编排也是您安全计划的一部分!

如果您要创建自己的虚拟机,尤其是如果您要构建自己的镜像,那将是很好的。我们将在下一章讨论如何构建自己的镜像。此外,我们将考虑推送这些镜像和在您的环境中部署它们的安全方面。

问题

  1. 如果要实施 firewalld,有哪些配置此防火墙的方法?

  2. 使用--permanent参数的firewall-cmd的原因是什么?

  3. 还有哪些选项可用于限制网络访问?

  4. 解释 DAC 和 MAC 之间的区别。

  5. 在 Azure 上运行的 VM 中使用 Linux 安全模块为什么很重要?

  6. 哪个 MAC 系统适用于哪个发行版?

  7. AppArmor 和 SELinux 之间的主要区别是什么?

  8. 在依赖和 Linux 配置方面,加入 Azure AD 域服务的要求是什么?

进一步阅读

与上一章类似,我强烈建议您访问第十一章,“故障排除和监视工作负载”,以了解有关 Linux 日志记录的信息,因为通常systemctl status命令提供的信息不足够。我也已经指向了 Lennart Poettering 的博客和 systemd 网站。

对于 Linux 安全性,您可以开始阅读 Donald A. Tevault 的书掌握 Linux 安全和加固。本章涵盖的许多主题以及其他许多主题都有详细的解释。

firewalld 守护程序有一个项目网站,firewalld.org,有博客和优秀的文档。对于较旧的发行版,Arch Linux 的维基是学习更多的好地方:wiki.archlinux.org/index.php/iptables。由于 iptables 被 firewalld 使用,所以在深入研究firewalld.richlanguage的 man 页面之前,这是一个很好的开始。

有关 SELinux 的所有细节都在 Red Hat 提供的指南中有所涵盖:access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/selinux_users_and_administrators_guide/虽然有点过时,但观看这个关于 SELinux 的 Red Hat 峰会的 YouTube 视频是一个很好的主意:www.youtube.com/watch?v=MxjenQ31b70

然而,要找到关于 AppArmor 的好信息更加困难。在gitlab.com/apparmor/apparmor/wikis/Documentation上有项目文档可用,Ubuntu 服务器指南是一个很好的起点。这可以在help.ubuntu.com/lts/serverguide/apparmor.html.en找到。

第七章:部署您的虚拟机

在 Azure 中部署单个虚拟机(VM)很容易,但一旦您想以一种单一的、可重复的方式部署更多的工作负载,您就需要某种自动化。

在 Azure 中,您可以使用 Azure 资源管理器(ARM)使用模板配置文件部署 VM,还可以使用 Azure CLI、PowerShell、Ruby 和 C#。本章后面将讨论用于创建 VM 映像的第三方工具,如 Packer 和 Vagrant。

所有这些部署方法或映像创建方法都使用 Azure 中的映像,但也可以创建自己的自定义 VM 并使用自定义映像。

在进入所有可能选项的配置之前,了解不同的部署选项以及为什么应该或不应该使用它们是很重要的。首先,您必须先问自己几个问题:

  • 您何时打算部署您的应用程序?

  • 工作负载的哪些部分应该是可重复的?

  • 工作负载的配置的哪些部分应该在部署期间完成?

所有这些问题将在本章结束时得到解答。以下是本章的主要要点:

  • 我们将讨论 Azure 中的自动化部署选项。

  • 我们将看到如何使用 Azure CLI 和 PowerShell 自动化部署。

  • 我们将介绍 Azure ARM 模板用于部署以及它们如何可以被重用于重新部署。

  • 将讨论 VM 映像创建工具,如 Packer 和 Vagrant。

  • 最后,我们将解释如何使用自定义映像并将我们自己的 VHD(虚拟硬盘)带入 Azure。

部署场景

介绍中提到的三个问题非常重要;这些可能因公司、应用程序和开发阶段而异。以下是一些部署场景的示例:

  • 应用程序是内部开发的,甚至可能是在您的本地计算机上。完成后,应用程序将在 Azure 中部署。更新将应用于正在运行的工作负载。

  • 这是相同的情景,但现在更新将通过部署新的 VM 来完成。

  • 应用程序由另一个供应商提供。

这三个示例非常常见,可能会影响您想要部署工作负载的方式。

你需要什么?

在跳入部署之前,您应该知道您需要什么,或者换句话说,需要哪些资源才能使您的应用程序正常工作。此外,Azure 中的所有内容都有限制和配额。一些限制是硬性的,有些可以通过联系微软支持来增加。要查看完整的 Azure 限制和配额列表,请访问docs.microsoft.com/en-us/azure/azure-subscription-service-limits

在部署之前,我们需要计划并确保我们的订阅限制不会阻碍我们的项目。如果有限制或限制,请联系微软支持并增加配额。但是,如果您正在使用免费试用,配额请求将不会被批准。您可能需要将部署移至您有足够配额来完成部署的地区。这些是我们将要部署的关键资源:

  • 一个资源组

  • 一个存储账户(未管理)或托管磁盘

  • 网络安全组

  • 一个虚拟网络

  • 虚拟网络的子网

  • 连接到 VM 的网络接口

关于 VM,您需要指定并考虑以下内容:

  • VM 大小

  • 存储

  • VM 扩展

  • 操作系统

  • 初始配置

  • 应用程序的部署

如果您看一下这些列表,您可能会想知道自动化部署是否是必要的或必需的。答案并不容易找到。让我们再次看看这些情景,并尝试找到答案。我们可以决定做以下事情:

  1. 创建一个 PowerShell 或 Bash 脚本来准备工作负载的 Azure 环境

  2. 创建第二个脚本来部署基于 Azure 中的一个提供的 VM,并使用 Azure VM 扩展来配置初始配置

  3. 使用像 Yum 这样的软件管理器部署应用程序

决定这样做没有错;这可能是您的最佳解决方案!然而,不管您喜不喜欢,都有依赖关系:

  • 您的操作系统是基于一个镜像部署的。这个镜像是由发布者提供的。如果镜像更新到您的应用程序不支持的版本会发生什么?

  • 这个镜像中已经完成了多少初始配置?还需要多少,谁控制这个镜像?

  • 这个镜像是否符合您的安全策略?

  • 如果出于任何原因您想离开 Azure,您能把您的应用程序迁移到其他地方吗?

Azure 中的自动化部署选项

在这个漫长的介绍之后,是时候看一下功能选项了,这些选项使得自动化部署您的工作负载成为可能:

  • 脚本编写

  • Azure 资源管理器

  • Ansible

  • Terraform

我们将在第八章,探索持续配置自动化中讨论 Ansible 和 Terraform。

脚本编写

自动化可以通过脚本完成。在 Azure 中,有许多由 Microsoft 支持的选项:

此外,您还可以使用 Java 和 C#等编程语言。也有社区项目;例如,github.com/capside/azure-sdk-perl 是一个构建 Perl 的完整 Azure SDK 的尝试。

所有语言都是有效的选择;选择您已经熟悉的语言。请注意,Ruby SDK 在撰写本书时处于预览状态。在预览状态下,语法可能会发生变化。

脚本编写特别适用于准备 Azure 环境。您还可以使用脚本来部署您的 VM,并且甚至可以使用 VM 扩展来包含初始配置。这是否是一个好主意取决于您的脚本能力、操作系统的基本镜像以及其中安装的软件版本。

使用脚本的最大反对意见是编写脚本很耗时。以下是一些可以帮助您高效编写脚本的提示:

  • 尽可能使用多个变量。这样,如果您需要在脚本中进行更改,您只需要更改变量的值。

  • 在循环中使用可识别的变量名,而不是像for i in这样的东西。

  • 特别是对于更大的脚本,声明可以重复使用的函数。

  • 有时,将变量(例如提供身份验证的变量)和函数放在单独的文件中是有意义的。通常一个脚本执行一个任务是个好主意。

  • 在您的代码中包含修改的时间戳,或者更好的是使用 Git 这样的版本控制系统。

  • 包含测试。例如,只有在资源不存在时才创建此资源。使用可读的退出代码。如果脚本无法部署资源,请使用类似无法创建$resource的内容,这样运行脚本的人就会明白脚本无法创建资源。

  • 包含足够的注释。如果您需要在一段时间后调试或重用脚本,您仍然会知道它的作用。不要忘记在标题中包含描述。

  • 在布局上花一些时间;使用缩进使代码易读。使用两个空格进行缩进,而不是制表符!

现在是举一个简短示例的时候了。这个示例将让您了解在部署虚拟机之前如何创建脚本来提供 Azure 所需的东西。

首先,声明变量。您也可以将变量添加到一个文件中,并让 PowerShell 加载这些变量。建议将它们存储在同一个脚本中,这样您可以随时返回并在需要时更新它们。

#Declare Variables
$myResourceGroup = "LinuxOnAzure" 
$myLocation = "West Europe" 
$myNSG = "NSG_LinuxOnAzure" 
$mySubnet = "10.0.0.0/24"
$myVnet= "VNET_LinuxOnAzure"

接下来,编写一个脚本来创建一个资源组。如果资源已经存在,脚本将跳过创建部分。如前所述,添加注释是使脚本可读的最佳实践,因此请使用#标记的注释,以便您了解代码块的作用:

# Test if the Resource Group already exists, if not: create it. 
Get-AzResourceGroup -Name $myResourceGroup -ErrorVariable notPresent -ErrorAction SilentlyContinue | out-null  
if ($notPresent) 
  { 
    # ResourceGroup doesn't exist, create it: 
    New-AzResourceGroup -Name $myResourceGroup -Location $myLocation     
    Write-Host "The Resource Group $myResourceGroup is created in the location $myLocation" 
  }  
else 
  { 
    Write-Host "The Resource Group $myResourceGroup already exists in the location $myLocation" 
  }

创建虚拟网络并配置子网:

#Test if the vnet name not already exists: 
Get-AzVirtualNetwork -Name $myVnet -ResourceGroupName $myResourceGroup -ErrorVariable notPresent -ErrorAction SilentlyContinue | out-null 
if ($notPresent) 
  { 
    # vnet doesn't exist, create the vnet

    $virtualNetwork = New-AzVirtualNetwork -ResourceGroupName $myResourceGroup -Location $myLocation -Name $myVnet -AddressPrefix 10.0.0.0/16
    # add subnet configuration
    $subnetConfig = Add-AzVirtualNetworkSubnetConfig -Name default -AddressPrefix $mySubnet -VirtualNetwork $virtualNetwork
    # Associate the subnet to the virtual network
    $virtualNetwork | Set-AzVirtualNetwork
     Write-Host "The virtual network $myVnet with $mySubnet configured is created in the location $myLocation" 
  }
else 
  { 
    Write-Host "The Resource Group $myVnet already exists in the location $myLocation" 
  }

这是创建网络安全组的一个示例:

# Create NSG
# Test if the Network Security Group does not already exist:  

Get-AzNetworkSecurityGroup -ResourceGroupName $myResourceGroup -Name $myNSG -ErrorVariable notPresent -ErrorAction SilentlyContinue | out-null 
if ($notPresent) 
{ 
# create the NSG 
$nsg = New-AzNetworkSecurityGroup -ResourceGroupName $myResourceGroup -Location $myLocation -Name $myNSG
# create the rules for SSH and HTTP 
$nsg | Add-AzNetworkSecurityRuleConfig -Name "allow_http" -Description "Allow HTTP" -Access Allow '
    -Protocol "TCP" -Direction Inbound -Priority 1002 -SourceAddressPrefix "*" -SourcePortRange * '
    -DestinationAddressPrefix * -DestinationPortRange 80 
$nsg | Add-AzNetworkSecurityRuleConfig -Name "allow_ssh" -Description "Allow SSH" -Access Allow '
    -Protocol "TCP" -Direction Inbound -Priority 1001 -SourceAddressPrefix "*" -SourcePortRange * '
    -DestinationAddressPrefix * -DestinationPortRange 22 
# Update the NSG.
  $nsg | Set-AzNetworkSecurityGroup
Write-Host "The NSG: $myNSG is configured is created with rules for SSH and HTTP in the resource group $myResourceGroup" 
} 
else 
{ 
Write-Host "The NSG $myNSG already existed in the resource group $myResourceGroup"  
}

到目前为止,您应该已经对如何创建脚本和虚拟网络有了一个很好的想法。正如在本节开头提到的,脚本编写并不是自动化部署的唯一手段;还有其他方法。在下一节中,我们将讨论如何使用 Azure 资源管理器模板来自动化部署。

使用 Azure 资源管理器进行自动化部署

第二章,开始使用 Azure 云中,我们将Azure 资源管理器ARM)定义如下:

“基本上,Azure 资源管理器使您能够使用诸如存储和虚拟机之类的资源。为此,您必须创建一个或多个资源组,以便您可以执行生命周期操作,例如在单个操作中部署、更新和删除资源组中的所有资源。”

从 Azure 门户或使用脚本,您可以做到上述所有的事情。但这只是其中的一小部分。您可以通过 ARM 使用模板部署 Azure 资源。微软提供了数百个快速启动模板,可在azure.microsoft.com/en-us/resources/templates找到。

当您通过 Azure 门户创建虚拟机时,甚至在创建之前就可以将该虚拟机下载为模板。如果您参考以下截图,您会发现即使在创建虚拟机之前,我们也有一个下载自动化模板的选项:

在仪表板中导航以创建并下载虚拟机作为模板

图 7.1:将虚拟机下载为模板

如果您点击下载自动化模板,您将会看到以下屏幕:

在模板窗格中添加脚本到库中

图 7.2:VM 模板窗格

正如您所看到的,您可以将脚本添加到 Azure 的库中,或者将此文件下载到您的本地计算机。您还将获得一个部署选项,通过它您可以更改参数并直接部署到 Azure。

脚本窗格中,Azure 给出了使用 PowerShell 和 CLI 进行部署的链接。

您可以轻松更改参数并部署新的虚拟机,或者重新部署完全相同的虚拟机。这与使用自己的脚本并没有太大的不同,但在开发方面更节省时间。

这并不是您可以使用 ARM 做的唯一事情;您可以配置 Azure 资源的每一个方面。例如,如果您通过 ARM 模板部署网络安全组,您可以像在 Azure 门户或通过 CLI 创建一样,定义一切,比如规则、端口范围和规则的优先级。创建自己的 ARM 模板并不那么困难。您需要 ARM 参考指南,可以在docs.microsoft.com/en-us/azure/templates找到。再加上这些示例,这是一个很好的入门资源。

另一种开始的方法是使用可在 Windows、Linux 和 macOS 上使用的 Visual Studio Code 编辑器,网址为code.visualstudio.comAzure 资源管理器工具扩展是必不可少的,如果您要开始使用 ARM,还有其他一些扩展,如Azure 帐户和登录Azure 资源管理器片段Azure CLI 工具。您可以开始使用现有模板,甚至可以将它们上传到 Cloud Shell,执行它们并对其进行调试。

要安装 Azure 资源管理器工具扩展,请按照以下步骤进行:

  1. 打开 Visual Studio Code。

  2. 从左侧菜单中选择扩展。或者,从查看菜单中选择扩展以打开扩展窗格。

  3. 搜索资源管理器

  4. Azure 资源管理器工具下选择安装

这是您找到安装选项的屏幕:

在 Visual Studio Code 上导航以安装 Azure 资源管理器工具

图 7.3:安装 Azure 资源管理器工具

Azure 中的另一个不错的功能是 ARM Visualizer,您可以在armviz.io找到它。它仍处于早期开发阶段。这是一个可以帮助您快速了解从 Quickstart 模板网站下载的 ARM 模板目的的工具。

除了下载模板,还可以将其保存到库中:

使用 ARM Visualizer 将模板保存到库中

图 7.4:将模板保存到库中

如此窗格所述,您可以通过在左侧导航栏中使用所有资源并搜索模板来轻松在 Azure 门户中导航:

在 Azure 门户上导航到模板

图 7.5:在 Azure 门户上导航到模板

您仍然可以在这里编辑您的模板!另一个不错的功能是,您可以与您的租户的其他用户共享您的模板。这可能非常有用,因为您可以创建一个只允许使用此模板进行部署的用户。

现在我们知道了如何从 Azure 门户部署模板,让我们看看如何可以使用 PowerShell 和 Bash 部署 ARM 模板。

使用 PowerShell 部署 ARM 模板

首先,验证模板格式是否正确,执行以下命令:

Test-AzResourceGroupDeployment -ResourceGroupName ExampleResourceGroup' -TemplateFile c:\MyTemplates\azuredeploy.json '
-TemplateParameterFile  c:\MyTemplates\storage.parameters.json

然后继续部署:

New-AzResourceGroupDeployment -Name <deployment name> -ResourceGroupName <resource group name> -TemplateFile c:\MyTemplates\azuredeploy.json
-TemplateParameterFile c:\MyTemplates\storage.parameters.json

使用 Bash 部署 ARM 模板

您还可以在部署之前验证您的模板和参数文件,以避免任何意外错误:

az group deployment validate \  
--resource-group ResourceGroupName \
   --template-file template.json \
   --parameters parameters.json

要部署,请执行以下命令:

az group deployment create \
  --name DeploymentName \
  --resource-group ResourceGroupName \
  --template-file template.json \
  --parameters parameters.json

现在我们已经部署了一个新的 VM,我们可以保留templates.jsonparameters.json,通过更改变量值可以重复使用它们。

假设我们已经删除了 VM,并且您希望重新部署它。您只需要 JSON 文件。如前所述,如果您已将模板存储在 Azure 中,您可以在那里找到重新部署的选项:

使用 JSON 文件重新部署 VM

图 7.6:使用 JSON 文件重新部署 VM

如果您希望通过 Azure CLI 或 PowerShell 完成相同的任务,请运行我们之前使用的命令,您的 VM 将准备好,配置与 ARM 模板中提到的相同。

初始配置

在部署工作负载之后,需要进行后部署配置。如果您想将其作为自动化解决方案的一部分来完成,那么有两个选项:

  • 自定义脚本扩展可以在部署后的任何时间使用。

  • cloud-init在引导期间可用。

使用自定义脚本扩展进行初始配置

在 VM 部署后,可以使用自定义脚本扩展执行后部署脚本。在前面的示例中,我们使用 ARM 模板部署了 VM。如果您想在部署后运行脚本怎么办?这就是自定义脚本扩展的作用。例如,假设您想部署一个 VM,并且在部署后,您想在其中安装 Apache 而无需登录到 VM。在这种情况下,我们将编写一个脚本来安装 Apache,并且将使用自定义脚本扩展在部署后安装 Apache。

该扩展将适用于除 CoreOS 和 OpenSUSE LEAP 之外的所有 Microsoft 认可的 Linux 操作系统。如果您使用的是除 Debian 或 Ubuntu 之外的发行版,则将脚本中的apt-get命令更改为您的发行版支持的软件管理器。

您可以使用 PowerShell 来配置扩展:

$myResourceGroup = "<resource group name>"
$myLocation = "<location>"
$myVM = "<vm name>"
$Settings = @{ "commandToExecute" = "apt-get -y install nginx";};
Set-AzVMExtension -VMName $myVM '
-ResourceGroupName $myResourceGroup'
-Location $myLocation '
-Name "CustomscriptLinux" -ExtensionType "CustomScript" '
-Publisher "Microsoft.Azure.Extensions" '
-typeHandlerVersion "2.0" -InformationAction SilentlyContinue '
-Verbose -Settings $Settings

PowerShell 输出将在配置后给出状态,即是否正常或出现了问题。运行脚本后,您可以在 VM 的日志中验证安装是否成功。由于我们正在 Ubuntu VM 上进行此操作,您可以通过检查/var/log/apt/history.log文件来验证 nginx 的安装。输出确认了 nginx 和所有其他依赖项都已安装:

检查日志以验证 nginx 安装

图 7.7:检查日志以验证 nginx 安装

您还可以提供脚本而不是命令。

让我们创建一个非常简单的脚本:

#!/bin/sh
apt-get install -y nginx firewalld
firewall-cmd --add-service=http
firewall-cmd --add-service=http --permanent

现在,脚本必须使用base64命令进行编码。您可以在任何 Linux VM 上执行此操作,或者您可以使用base64字符串:

cat nginx.sh| base64

注意

在某些版本的 base64 中,您必须添加-w0参数以禁用换行。只需确保它是一行!

$Settings变量将如下所示:

$Settings = @{"script" = "<base64 string>";};

由于我们已经使用第一个脚本安装了 nginx,您可以使用apt purge nginx来删除 ngnix,或者您可以完全创建一个新的 VM。与之前一样,我们可以去检查历史日志:

检查 nginx 的历史日志

图 7.8:检查历史日志

日志条目清楚地显示了apt install –y nginx firewalld已被执行。由于我们正在查看 apt 历史记录,我们将无法确认是否添加了 firewalld HTTP 规则。要确认这一点,您可以运行firewall-cmd –list-services

验证 firewalld 规则

图 7.9:检查是否添加了 firewalld HTTP 规则

如果需要,脚本可以被压缩或上传到存储 blob 中。

当然,您可以使用 Azure CLI 进行初始配置。在这种情况下,您必须提供类似于此的 JSON 文件:

{
    "autoUpgradeMinorVersion": true,
    "location": "<location>",
    "name": "CustomscriptLinux",
    "protectedSettings": {},
    "provisioningState": "Failed",
    "publisher": "Microsoft.Azure.Extensions",
    "resourceGroup": "<resource group name>",
    "settings": {
      "script": "<base64 string"
    },
    "tags": {},
    "type": "Microsoft.Compute/virtualMachines/extensions",
    "typeHandlerVersion": "2.0",
    "virtualMachineExtensionType": "CustomScript"
  }

然后,执行以下az命令:

az vm extension set --resource-group <resource group> \
  --vm-name <vm name> \
  --name customScript --publisher Microsoft.Azure.Extensions \
  --settings ./nginx.json

注意

JSON 文件可以包含在 ARM 模板中。

如果您正在使用 PowerShell 或 Azure CLI 进行调试目的,/var/log/azure/custom-script目录包含您的操作日志。

使用 cloud-init 进行初始配置

自定义 VM 扩展的问题在于脚本可能非常特定于发行版。您已经可以在使用的示例中看到这一点。如果使用不同的发行版,您将需要多个脚本,或者您将需要包含发行版检查。

在部署 VM 后进行一些初始配置的另一种方法是使用 cloud-init。

cloud-init 是一个由 Canonical 创建的项目,旨在为定制云映像提供云解决方案和 Linux 发行版不可知的方法。在 Azure 中,它可以与映像一起使用,以在第一次引导期间或创建 VM 时准备操作系统。

并非所有得到 Microsoft 认可的 Linux 发行版都受支持;Debian 和 SUSE 根本不受支持,而且在最新版本的发行版可以使用之前通常需要一些时间。

cloud-init 可用于运行 Linux 命令和创建文件。cloud-init 中有可用的模块来配置系统,例如安装软件或进行一些用户和组管理。如果有可用的模块,那么这是最好的方法。它不仅更容易(为您完成了艰苦的工作),而且还与发行版无关。

cloud-init 使用 YAML;请注意缩进很重要!脚本的目的是安装npmnodejsnginx软件包,然后配置 nginx,最后显示消息Hello World from host $hostname,其中$hostname是 VM 的名称。首先,让我们创建一个 YAML 文件,内容如下,并将其命名为cloudinit.yml

#cloud-config
groups: users
users:
  - default
  - name: azureuser
  - groups: users
  - shell: /bin/bash
package_upgrade: true
packages:
  - nginx
  - nodejs
  - npm
write_files:
  - owner: www-data:www-data
  - path: /etc/nginx/sites-available/default
    content: |
      server {
        listen 80;
        location / {
          proxy_pass http://localhost:3000;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection keep-alive;
          proxy_set_header Host $host;
          proxy_cache_bypass $http_upgrade;
        }
      }
  - owner: azureuser:users
  - path: /home/azureuser/myapp/index.js
    content: |
      var express = require('express')
      var app = express()
      var os = require('os');
      app.get('/', function (req, res) {
        res.send('Hello World from host ' + os.hostname() + '!')
      })
      app.listen(3000, function () {
        console.log('Hello world app listening on port 3000!')
      })
runcmd:
  - systemctl restart nginx
  - cd "/home/azureuser/myapp"
  - npm init
  - npm install express -y
  - nodejs index.js

如果您查看此配置文件,您可以看到以下模块的一些使用情况:

  • usersgroups:用户管理

  • packagespackage_upgrade:软件管理

  • write_files:文件创建

  • runcmd:运行模块无法实现的命令

您还可以创建一个 VM:

az vm create --resource-group <resource group> \
  --name <vm name> --image UbuntuLTS \
  --admin-username linuxadmin \
  --generate-ssh-keys --custom-data cloudinit.txt

部署后,需要一些时间才能完成所有工作。日志记录在 VM 的/var/log/cloud-init.log/var/log/cloud-init-output.log文件中。

更改网络安全组规则以允许端口80上的流量。之后,打开浏览器到 VM 的 IP 地址。如果一切正常,它将显示以下内容:Hello World from host ubuntu-web!

注意

Az cmdlets 不支持 cloud-init。

Vagrant

到目前为止,我们使用了 Microsoft 提供的解决方案;也许我们应该称它们为本地解决方案。这不是在 Azure 中部署工作负载的唯一方法。许多供应商已经创建了在 Azure 中自动化部署的解决方案。在本节中,我们想介绍来自名为 HashiCorp 的公司的解决方案(www.hashicorp.com)。在本章的后面,我们将介绍该公司的另一款产品:Packer。我们选择这些产品有几个原因:

  • 这些产品非常受欢迎和知名。

  • Microsoft 和 HashiCorp 之间有着良好的关系;他们一起努力实现越来越多的功能。

  • 而且最重要的原因是:HashiCorp 有不同的产品,可以用于不同的实施场景。这将让您再次考虑在不同的用例中选择什么方法。

Vagrant 是开发人员可以用来部署的工具。它可以帮助您以标准化的方式设置环境,以便您可以一遍又一遍地重新部署。

安装和配置 Vagrant

Vagrant 适用于多个 Linux 发行版、Windows 和 macOS,并可从www.vagrantup.com/downloads.html下载:

  1. 要在 Ubuntu 中安装软件,请使用以下命令:
cd /tmp
wget \ https://releases.hashicorp.com/vagrant/2.1.2/vagrant_2.1.2_x86_64.deb
sudo dpkg -i vagrant_2.1.2_x86_64.deb

在 RHEL/CentOS 中,使用以下命令:

sudo yum install \
 https://releases.hashicorp.com/vagrant/2.1.2/ \
 vagrant_2.1.2_x86_64.rpm

如果您将其部署在单独的 VM 或工作站上,请确保您也安装了 Azure CLI。

登录到 Azure:

az login

创建一个服务主体帐户,Vagrant 可以用来进行身份验证:

az ad sp create-for-rbac --name vagrant

从输出中,您需要appID,也称为客户端 ID,以及密码,它与客户端密钥相同。

  1. 执行以下命令以获取您的租户 ID 和订阅 ID:
az account show

在此命令的输出中,您可以看到您的租户 ID 和订阅 ID。

  1. 创建一个具有以下内容的文件,并将其保存到~/.azure/vagrant.sh
AZURE_TENANT_ID="<tenant id>"
AZURE_SUBSCRIPTION_ID="<account id>"
AZURE_CLIENT_ID="<app id>"
AZURE_CLIENT_SECRET="<password>"
export AZURE_TENANT_ID AZURE_SUBSCRIPTION_ID AZURE_CLIENT_ID\
  AZURE_CLIENT_SECRET
  1. 在使用 Vagrant 之前,必须导出这些变量。在 macOS 和 Linux 中,您可以通过执行以下命令来实现:
source <file>
  1. 必须有一个 SSH 密钥对可用。如果尚未完成此操作,请使用此命令创建密钥对:
ssh-keygen
  1. 最后一步涉及安装 Vagrant 的 Azure 插件:
vagrant plugin install vagrant-azure
  1. 验证安装:
vagrant version

使用版本命令验证 vagrant 安装

图 7.10:验证 vagrant 安装

现在我们已经确认 Vagrant 已经启动运行,让我们继续使用 Vagrant 部署一个 VM。

使用 Vagrant 部署虚拟机

要使用 Vagrant 部署虚拟机,你需要创建一个新的工作目录,在那里我们将创建Vagrantfile

Vagrant.configure('2') do |config|  
config.vm.box = 'azure'  
# use local ssh key to connect to remote vagrant box  config.ssh.private_key_path = '~/.ssh/id_rsa'  
config.vm.provider :azure do |azure, override|       
azure.tenant_id = ENV['AZURE_TENANT_ID']    
azure.client_id = ENV['AZURE_CLIENT_ID']    
azure.client_secret = ENV['AZURE_CLIENT_SECRET']    azure.subscription_id = ENV['AZURE_SUBSCRIPTION_ID']  
end
end

配置文件以一个声明开始,我们需要之前安装的 Vagrant 的 Azure 插件。之后,VM 的配置开始。为了能够使用 Vagrant 提供工作负载,需要一个虚拟 box。它几乎是一个空文件:只注册 Azure 作为提供者。要获得这个虚拟 box,执行以下命令:

vagrant box add azure-dummy\
  https://github.com/azure/vagrant-azure/raw/v2.0/dummy.box\
  --provider azure

通常,很多选项,比如vm_image_urn,将被嵌入到一个 box 文件中,你只需要在Vagrantfile中提供最少的选项。由于我们使用的是一个虚拟 box,没有预先配置的默认值。az.vm_image_urn是 Azure 提供的实际镜像,语法如下:

 <publisher>:<image>:<sku>:<version>

除了使用标准镜像,还可以使用自定义虚拟硬盘VHD)文件,使用这些指令:

  • vm_vhd_uri

  • vm_operating_system

  • vm_vhd_storage_account_id

在本章的后面,我们将更详细地讨论这些自定义 VHD 文件。

另一个重要的值是虚拟机的名称;它也被用作 DNS 前缀。这必须是唯一的!否则,你会得到这个错误:DNS 记录<name>.<location>.cloudapp.azure.com 已经被另一个公共 IP 使用了

部署 Vagrant box,虚拟机:

vagrant up 

输出应该是这样的:

使用 up 命令部署 vagrant box

图 7.11:部署 vagrant box

当机器准备好使用时,你可以使用这个命令登录:

vagrant ssh

你的工作目录的内容被复制到 VM 中的/vagrant。这可以是一个非常好的方式,让你的文件在 VM 中可用。

使用这个命令清理你的工作:

vagrant destroy

注意

也可以创建多台虚拟机。

Vagrant Provisioners

提供一种简单的方式来部署虚拟机并不是 Vagrant 最重要的特性。使用 Vagrant 的主要原因是让一个完整的环境运行起来;部署后,虚拟机需要配置。有 provisioners 来完成后续工作。provisioners 的目的是进行配置更改,自动安装软件包等。你可以使用 shell provisioner,在客户端 VM 中上传和执行脚本,以及文件 provisioner 来运行命令并将文件复制到 VM 中。

另一个可能性是使用 Vagrant provisioners 来进行编排工具,比如 Ansible 和 Salt。下一章将讨论这些工具。在本章中,连同 Vagrant 网站上的 provisioners 文档(www.vagrantup.com/docs/provisioning/),我们将配置 shell provisioners 和文件 provisioner。让我们继续通过将以下代码块添加到Vagrantfile来配置 provisioners。

将这段代码添加到Vagrantfile的底部:

# Configure the Shell Provisioner
config.vm.provision "shell", path: "provision.sh"
end # Vagrant.config

我们在 shell provisioner 中引用了一个文件provision.sh。所以让我们创建一个简短的provision.sh脚本,包含一些简单的命令:

#!/bin/sh
touch /tmp/done
touch /var/lib/cloud/instance/locale-check.skip

再次部署 VM,你会看到 Vagrant 已经接受了我们创建的 SSH 密钥,并开始了配置:

再次部署 VM 以使 Vagrant 接受 SSH 密钥并开始配置

图 7.12:Vagrant 已开始配置

执行这段代码来验证在 VM 中是否已经创建了/tmp/done目录,就像我们在provision.sh文件中指示的那样:

vagrant ssh -c "ls -al /tmp/done"

Packer

对于开发人员来说,尤其是如果有许多人在同一应用上工作,拥有标准化的环境非常重要。如果您不使用容器技术(请参阅第九章Azure 中的容器虚拟化,以及第十章使用 Azure Kubernetes 服务,了解有关此技术的更多信息),Vagrant 是一个很好的工具,它可以帮助开发人员管理虚拟机的生命周期,以便以可重复的方式快速启动应用程序。它根据镜像提供或自定义 VHD 进行配置。如果您想在云中开发应用程序,这就是您所需要的一切。

但是,如果您想要更复杂的环境、构建自己的镜像、多机部署、跨云环境等,这并非完全不可能,但一旦尝试,您会发现 Vagrant 并不适用于这些场景。

这就是另一个 HashiCorp 产品 Packer 派上用场的地方。在本节中,我们将使用与之前与 Vagrant 相似的配置来使用 Packer。

安装和配置 Packer

Packer 可用于 macOS、Windows、多个 Linux 发行版和 FreeBSD。可在www.packer.io/downloads.html下载软件包。

下载软件包,解压缩,然后就可以使用了。在 Linux 中,最好创建一个~/.bin目录并在那里解压缩:

mkdir ~/bin
cd /tmp
wget wget https://releases.hashicorp.com/packer/1.2.5/\
  packer_1.2.5_linux_amd64.zip
unzip /tmp/packer*zip
cp packer ~/bin 

注销并重新登录。几乎每个发行版都会在~/bin目录可用时将其添加到PATH变量中,但您必须注销并重新登录。

通过执行$PATH检查PATH变量。如果您无法看到bin文件夹添加到路径中,请执行以下操作:

export PATH=~/bin:$PATH

验证安装:

packer version

如果安装成功,该命令将返回 Packer 的版本,如图中所示:

通过 Packer 版本验证 Packer 安装

图 7.13:通过 Packer 版本验证 Packer 安装

对于 Packer 的配置,我们将需要与 Vagrant 相同的信息:

  • Azure 租户 ID(az account show

  • Azure 订阅 ID(az account show

  • 服务主体帐户的 ID(如果要使用与 Vagrant 相同的帐户,请使用az app list --display-name vagrant命令)

  • 此帐户的秘密密钥(如果需要,可以使用az ad sp reset-credentials命令生成新的密钥)

  • 在正确的位置中存在的资源组;在此示例中,我们使用LinuxOnAzure作为资源组名称,West Europe作为位置(使用az group create --location "West Europe" --name "LinuxOnAzure"命令创建)

创建一个文件(例如/packer/ubuntu.json),其中包含以下内容:

{ 
    "builders": [{ 
      "type": "azure-arm", 
      "client_id": "<appId>", 
      "client_secret": "<appPassword>", 
      "tenant_id": "<tenantId>", 
      "subscription_id": "<subscriptionID>", 
      "managed_image_resource_group_name": "LinuxOnAzure", 
      "managed_image_name": "myPackerImage", 
      "os_type": "Linux", 
      "image_publisher": "Canonical",
      "image_offer": "UbuntuServer", 
      "image_sku": "18.04-LTS", 
      "location": "West Europe", 
      "vm_size": "Standard_B1s" 
    }], 
    "provisioners": [{ 
   "type": "shell", 
   "inline": [ 
   "touch /tmp/done", 
   "sudo touch /var/lib/cloud/instance/locale-check.skip" 
   ] 
    }] 
  }

验证语法:

packer validate ubuntu.json

然后,按以下方式构建镜像:

packer build ubuntu.json

使用 Packer 构建命令构建镜像

图 7.14:使用 Packer 构建命令构建镜像

Packer 需要一些时间来构建虚拟机,运行配置程序并清理部署。

构建完成后,Packer 将为您提供构建的摘要,例如资源组、虚拟机部署位置、镜像名称和位置:

Packer 提供的图像摘要,构建完成后

图 7.15:镜像摘要

构建将创建一个镜像,但不会创建运行中的虚拟机。从 Packer 创建的镜像中,您可以使用以下命令部署虚拟机:

az vm create \ 
--resource-group LinuxOnAzure \
 --name mypackerVM \ 
--image myPackerImage \ 
--admin-username azureuser \ 
--generate-ssh-keys

要清理环境并删除 Packer 创建的镜像,请执行以下命令:

az resource delete --resource-group LinuxOnAzure --resource-type images \
  --namespace Microsoft.Compute --name myPackerImage

我在本章前面提供的 JSON 文件足以创建镜像。这与我们在 Vagrant 中所做的非常相似,但为了将其转换为可部署的镜像,我们必须将 VM 进行泛化,这意味着允许它为多个部署进行镜像化。将/usr/sbin/waagent -force -deprovision+user & export HISTSIZE=0 && sync添加到代码中将泛化 VM。不要担心这段代码-在下一节中,当我们通过 Azure CLI 泛化 VM 时,您将再次看到它。

找到以下代码:

 "provisioners": [{
    "type": "shell",
    "inline": [
      "touch /tmp/done",
      "sudo touch /var/lib/cloud/instance/locale-check.skip"
    ]

这需要用以下代码替换:

     "provisioners": [{
    "type": "shell",
    "execute_command": "echo '{{user 'ssh_pass'}}' | {{ .Vars }} sudo -S -E sh '{{ .Path }}'",
    "inline": [
     "touch /tmp/done",
     "touch /var/lib/cloud/instance/locale-check.skip",
     "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"
    ]
    }]
  }

execute_command是用于以正确用户身份执行脚本的命令。

使用packer validate命令验证模板,以避免任何错误并重新构建镜像。

到目前为止,我们已经使用 Packer 创建了镜像,但也可以使用 Azure CLI 和 Powershell 来完成。接下来的部分将详细介绍这一点。

自定义虚拟机和 VHD

在上一节中,我们在 Azure 中使用了标准 VM 提供,并使用了两种不同的方法进行了一些配置工作。然而,正如之前所述,存在一些原因使得默认镜像可能不适合您。让我们再次总结一下原因。

Azure 的本机镜像提供了部署 VM 的良好起点。使用本机镜像的一些好处如下:

  • 由 Linux 发行版供应商或可信赖的合作伙伴创建和支持

  • 快速部署,无论是手动还是编排,当然,您之后可以自定义它们

  • 使用 Azure 扩展功能和选项轻松扩展

如果您选择使用本机提供的服务,那么也会有一些缺点或者说一些不足之处:

  • 如果您需要比标准镜像更加安全的镜像,那么您必须依赖于市场上昂贵的加固镜像版本。

  • 标准镜像不符合公司标准,尤其是在分区方面。

  • 标准镜像并非针对特定应用进行了优化。

  • 一些 Linux 发行版不受支持,例如 Alpine 和 ArchLinux。

  • 关于可重现环境的问题:某个镜像版本可用多长时间?

因此,我们需要自定义镜像,以便我们可以自定义镜像并减轻问题或不足之处。我们并不是在暗示本机提供不安全或无法完成任务,但在企业环境中,存在一些情况,例如为 RHEL/SLES VMs 提供自己的订阅和作为镜像打包的第三方独立软件供应商ISV)软件,您必须使用自定义镜像。让我们继续看看如何在 Azure 中使用自定义镜像。

创建托管镜像

在上一节中,我们调查了 Packer。创建了一个 VM,然后将其转换为镜像。此镜像可用于部署新的 VM。这种技术也称为捕获 VM 镜像

让我们看看是否可以逐步使用 Azure CLI 手动进行操作:

  1. 创建资源组:
myRG=capture
myLocation=westus
az group create --name $myRG --location $myLocation
  1. 创建 VM:
myVM=ubuntudevel
AZImage=UbuntuLTS
Admin=linvirt
az vm create --resource-group $myRG  --name $myVM \
  --image $AZImage \
  --admin-username linvirt  --generate-ssh-keys
  1. 登录到 VM 并使用 Azure VM Agent 取消配置它。它通过删除特定于用户的数据来泛化 VM:
sudo waagent -deprovision+user

执行命令后,输出将显示有关即将删除的数据的警告。您可以输入y继续,如下所示:

使用 VM Agent 取消配置 VM

图 7.16:取消配置 VM

输入exit退出 SSH 会话。

  1. 释放 VM:
az vm deallocate --resource-group $myRG --name $myVM
  1. 将其标记为泛化。这意味着允许它为多个部署进行镜像化:
az vm generalize --resource-group $myRG --name $myVM
  1. 从此资源组中的 VM 创建镜像:
destIMG=customUbuntu
az image create --resource-group $myRG --name $destIMG --source $myVM
  1. 验证结果:
az image list -o table

输出将以表格格式显示镜像列表:

以表格格式列出的 Azure 镜像列表

图 7.17:Azure 镜像列表
  1. 您可以使用此镜像部署新的 VM:
az vm create --resource-group <resource group> \
  --name <vm name> \ 
  --image $destIMG \    
  --admin-username <username> \    
  --generate-ssh-key

如果您在 PowerShell 中,这也是可能的。让我们非常快速地通过第一步。流程非常相似;唯一的区别是我们使用 PowerShell cmdlet:

$myRG="myNewRG" 
$myLocation="westus" 
$myVM="ubuntu-custom" 
$AZImage="UbuntuLTS"

#Create resource group
New-AzResourceGroup -Name $myRG -Location $myLocation 

#Create VM
New-AzVm '
-ResourceGroupName $myRG '
-Name $myVM '
-ImageName $AZimage '
-Location $myLocation '
-VirtualNetworkName "$myVM-Vnet" '
-SubnetName "$myVM-Subnet" '
-SecurityGroupName "$myVM-NSG" '
-PublicIpAddressName "$myVM-pip"

PowerShell 可能提示您输入凭据。继续输入凭据以访问您的 VM。之后,我们将继续对 VM 进行去配置:

Stop-AzureRmVM -ResourceGroupName <resource group>'
  -Name <vm name> 

与之前一样,现在我们必须将 VM 标记为通用化:

Set-AzVm -ResourceGroupName <resource group> -Name <vm name> '
 -Generalized

让我们捕获 VM 信息并将其保存到一个变量中,因为我们将需要它来创建图像的配置:

$vm = Get-AzVM –Name <vm name> -ResourceGroupName <resource group name>

现在让我们创建图像的配置:

$image = New-AzImageConfig -Location<location> -SourceVirtualMachineId $vm.Id

因为我们在$image中存储了配置,所以使用它来创建图像:

New-AzImage -Image $image -ImageName <image name> '
 -ResourceGroupName <resource group name>

验证图像是否已创建:

Get-AzImage –ImageName <Image Name>

运行上述命令将为您提供类似以下的输出,其中包含您创建的图像的详细信息:

使用 Get-AzImage –ImageName 命令获取的图像详细信息摘要

图 7.18:获取图像详细信息

如果您想使用刚刚创建的图像创建 VM,请执行以下命令:

New-AzVm ' 
-ResourceGroupName "<resource group name>" ' 
-Name "<VM Name>" ' 
-ImageName "<Image Name>" ' 
-Location "<location>" ' 
-VirtualNetworkName "<vnet name>" ' 
-SubnetName "<subnet name>" ' 
-SecurityGroupName "<nsg name>" ' 
-PublicIpAddressName "<public IP name>"

总结我们所做的,我们创建了一个 VM,通用化了它,并创建了一个可以进一步用于部署多个 VM 的图像。还有一种从参考图像创建多个 VM 的替代方法,即使用''快照''。这将在下一节中介绍。

使用快照的备用方法

如果您想保留原始 VM,可以从快照创建 VM 图像。Azure 中的快照实际上是一个完整的 VM!

使用 PowerShell

  1. 声明一个变量$vm,它将存储有关我们将要获取和创建快照的 VM 的信息:
$vm = Get-AzVm -ResourceGroupName <resource group> '
  -Name $vmName
$snapshot = New-AzSnapshotConfig '   
  -SourceUri $vm.StorageProfile.OsDisk.ManagedDisk.Id '   
  -Location <location> -CreateOption copy
New-AzSnapshot '    
  -Snapshot $snapshot -SnapshotName <snapshot name> '    
  -ResourceGroupName <resource group>
  1. 因为我们需要快照 ID 用于后续步骤,所以我们将重新初始化快照变量:
$snapshot = Get-AzSnapshot –SnapshotName <Snapshot Name>
  1. 下一步涉及从快照创建图像配置。
$imageConfig = New-AzImageConfig -Location <location>

$imageConfig = Set-AzImageOsDisk -Image $imageConfig '
 -OsState Generalized -OsType Linux -SnapshotId $snapshot.Id
  1. 最后,创建图像:
New-AzImage -ImageName <image name> '
  -ResourceGroupName <resource group> -Image $imageConfig

使用 Azure CLI

在 Azure CLI 中,事情更容易;只需获取快照的 ID 并将其转换为磁盘:

  1. 使用 Azure CLI 创建快照:
disk=$(az vm show --resource-group <resource group>\
  --name <vm name> --query "storageProfile.osDisk.name" -o tsv)
az snapshot create --resource-group <resource group>\
  --name <snapshot name> --source $disk
  1. 创建图像:
snapshotId=$(az snapshot show --name <snapshot name>\
  --resource-group <resource group> --query "id" -o tsv)
az image create --resource-group <resource group> --name myImage \
  --source $snapshotID --os-type Linux 

在对 VM 进行快照之前不要忘记通用化 VM。如果您不想这样做,可以从快照创建磁盘,并将其用作 Azure CLI 中的--attach-os-disk命令的磁盘参数,或者在 PowerShell 中使用Set-AzVMOSDisk

自定义 VHD

您可以完全从头开始构建自己的图像。在这种情况下,您必须构建自己的 VHD 文件。有多种方法可以做到这一点:

  • 在 Hyper-V 或 VirtualBox 中创建一个 VM,它是 Windows、Linux 和 macOS 可用的免费 hypervisor。这两种产品都原生支持 VHD。

  • 在 VMware Workstation 或 KVM 中创建您的 VM,并在 Linux qemu-img中使用它来转换图像。对于 Windows,可以在www.microsoft.com/en-us/download/details.aspx?id=42497下载 Microsoft Virtual Machine Converter。这包括一个 PowerShell cmdlet,ConvertTo-MvmcVirtualHardDisk,用于进行转换。

注意

Azure 仅支持 Type-1 VHD 文件,并且应该具有与 1 MB 对齐的虚拟大小。在撰写本书时,Type-2 正在预览中(docs.microsoft.com/en-us/azure/virtual-machines/windows/generation-2)。

Azure 在 Hyper-V 上运行。Linux 需要特定的内核模块才能在 Azure 中运行。如果 VM 是在 Hyper-V 之外创建的,Linux 安装程序可能不包括 Hyper-V 驱动程序在初始 ramdisk(initrdinitramfs)中,除非 VM 检测到它正在运行在 Hyper-V 环境中。

当使用不同的虚拟化系统(如 VirtualBox 或 KVM)来准备您的 Linux 图像时,您可能需要重建initrd,以便至少hv_vmbushv_storvsc内核模块在初始 ramdisk 上可用。这个已知问题适用于基于上游 Red Hat 发行版的系统,可能也适用于其他系统。

重建initrdinitramfs映像的机制可能因发行版而异。请查阅您发行版的文档或支持以获取正确的操作步骤。以下是使用mkinitrd实用程序重建initrd的示例:

  1. 备份现有的initrd映像:
cd /boot
sudo cp initrd-'uname -r'.img  initrd-'uname -r'.img.bak
  1. 使用hv_vmbushv_storvsc 内核模块重建initrd
sudo mkinitrd --preload=hv_storvsc --preload=hv_vmbus -v -f initrd-'uname -r'.img 'uname -r'

几乎不可能描述每个 Linux 发行版和每个 hypervisor 的所有可用选项。总的来说,您需要做的事情在这里列出。非常重要的是我们要准确地按照步骤进行,否则无法完成此任务。我们强烈建议按照 Microsoft 的文档进行操作(docs.microsoft.com/en-us/azure/virtual-machines/linux/create-upload-generic)。

  1. 修改 GRUB 或 GRUB2 中的内核引导行,以包括以下参数,以便所有控制台消息都发送到第一个串行端口。这些消息可以帮助 Azure 支持调试任何问题:
console=ttyS0,115200n8 earlyprintk=ttyS0,115200 rootdelay=300
  1. Microsoft 还建议删除以下参数(如果存在):
rhgb quiet crashkernel=auto
  1. 安装 Azure Linux 代理,因为代理是在 Azure 上为 Linux 映像进行配置所必需的。您可以使用rpmdeb文件安装它,或者您可以按照 Linux 代理指南中提供的步骤手动安装它(docs.microsoft.com/en-us/azure/virtual-machines/extensions/agent-linux)。

  2. 确保安装了 OpenSSH 服务器并在启动时自动启动。

  3. 不要创建交换。如果需要,稍后可以启用它,就像我们在前一章中讨论的那样。

  4. 取消配置 VM,如创建托管映像部分所述。

  5. 关闭 VM,您的 VHD 已准备好上传到 VM。

为简单起见,我们将跳过前面的步骤,并从 Ubuntu 的云映像存储库下载官方映像,因为最重要的部分是将映像上传到 Azure。从cloud-images.ubuntu.com/bionic/下载云映像。此网页包含所有 Bionic 的版本,您可以浏览目录并下载 Azure 的 tar.gz 文件。文件名类似于bionic-server-cloudimg-amd64-azure.vhd.tar.gz;但是,这个名称可能会根据您查看的版本有所不同。

现在我们必须将 VHD 上传到 Azure:

  1. 首先,为映像准备一个单独的存储帐户是个好主意,所以让我们创建一个新的存储帐户。在这里,我们选择Premium_LRS,但如果您愿意,也可以选择Standard_LRS以节省一些成本:
az storage account create --location <location> \
  --resource-group <resource group> --sku Premium_LRS \
  --name <account name> --access-tier Cool --kind StorageV2
  1. 保存输出以备后用。列出访问密钥:
az storage account keys list --account-name <storage account name>\
  --resource-group <resource group>
  1. 再次保存输出。我们需要的下一步是创建一个容器来存储文件:
 az storage container create \
  --account-name <storage account>\
  --account-key <storage account key 1>  
  --name <container name> 
  1. 现在您可以上传 VHD:
az storage blob upload --account-name <storage account>\
  --account-key <storage account key> \
  --container-name <container name> \ 
  --type page --file ./bionic-server-cloudimg-amd64.vhd \
  --name bionic.vhd

注意

您还可以使用 Azure 门户或 PowerShell 上传文件。其他方法包括 Azure 存储资源管理器(azure.microsoft.com/en-us/features/storage-explorer/)或 Azure VHD 工具(github.com/Microsoft/azure-vhd-utils)。最后一个方法速度非常快!

  1. 接收 blob URL:
az storage blob url --account-name <storage account> \
  --account-key <storage account key> \
  --container-name <container name> \
  --name bionic.vhd
  1. 现在可以从上传创建一个磁盘:
az disk create --resource-group <resoure group> \
 --name bionic --source <blob url> --Location <location>
  1. 使用此磁盘创建 VM 映像:
az image create --resource-group <resource group> \
  --name bionic --source <blob url> --os-type linux 
  --location <location>
  1. 最后,基于此映像创建一个 VM:
az vm create --resource-group <resource group> \
 --name <vm name> \ 
 --image bionic \    
 --admin-username <username> \    
 --generate-ssh-key \
 --location <location>

注意

您可以将 VHD 映像设为公共;一个很好的例子是一个名为 NixOS 的鲜为人知的 Linux 发行版。在他们的网站上,nixos.org/nixos/download.html,他们描述了在 Azure 中部署其操作系统的方法!

让我们总结一下我们所做的。我们采取了两种方法。我们从现有的 VM 创建并上传了一个 Linux VHD,然后手动下载了一个 Ubuntu VHD 并使用它。无论哪种方式,我们都将把它上传到存储账户,并将使用它创建一个镜像。这个镜像是可重复使用的,你可以部署任意多个 VM。

自动化过程和可用工具是广泛的。在下一章中,我们将继续讨论自动化过程,并讨论最广泛使用的工具,即 Ansible 和 Terraform。

总结

在本章中,我们开始思考在 Azure 中为什么以及何时应该使用自动化。随后,我们添加了关于使用 Azure 提供的镜像的问题。

考虑到这些问题,我们探讨了自动化部署的选项:

  • 脚本编写

  • ARM 模板

  • Vagrant

  • Packer

  • 构建和使用自己的镜像

Vagrant 和 Packer 是第三方解决方案的例子,它们是非常受欢迎的工具,可以轻松地创建和重新创建环境,作为你的开发过程的重要部分。

重要的是要知道,本章中描述的所有技术都可以组合成一个完整的解决方案。例如,你可以将 cloud-init 与 ARM 一起使用,也可以与 Vagrant 一起使用。

自动化和编排是密切相关的。在本章中,我们讨论了自动化,特别是作为开发环境的一部分,用于自动化 VM 的部署。自动化通常是一个难以维护的解决方案,用于跟踪开发和部署的工作负载。这就是编排发挥作用的地方,下一章将涵盖这一点。

问题

  1. 在 Azure 中使用自动化部署的主要原因是什么?

  2. 在开发环境中自动化的目的是什么?

  3. 你能描述脚本编写和自动化之间的区别吗?

  4. 你能说出 Azure 中可用的一些自动化部署选项吗?

  5. Vagrant 和 Packer 有什么区别?

  6. 为什么应该使用自己的镜像而不是 Azure 提供的镜像?

  7. 有哪些选项可以创建自己的镜像?

也许你可以抽出一些时间来完成脚本编写部分的示例脚本,用你选择的语言。

进一步阅读

特别是关于 Azure CLI、PowerShell 和 ARM,Azure 文档包含大量有价值的信息和许多示例。我们在第二章,开始使用 Azure 云进一步阅读部分中写的一切对本章也很重要。

微软提供的另一个资源是其博客。如果你访问blogs.msdn.microsoft.com/wriju/category/azure/,你会发现许多关于自动化的有趣帖子,包括更详细的示例。

在他的博客michaelcollier.wordpress.com中,Michael S. Collier 提供了大量关于 Azure 的信息。几乎每篇帖子都包括脚本编写和自动化的可能性。

关于 Vagrant 并没有太多最近的书。我们相信你会喜欢一年前出版的Infrastructure as Code (IAC) Cookbook,作者是 Stephane Jourdan 和 Pierre Pomes。这本书不仅涉及 Vagrant;它还涵盖了其他解决方案,如 cloud-init 和 Terraform。作者创作了一本不仅是很好的介绍,而且还能用作参考指南的书。

我们可以推荐一本最近出版的书吗?Hands-On DevOps with Vagrant: Implement End-to-End DevOps and Infrastructure Management Using Vagrant,作者是 Alex Braunton。他在 YouTube 上关于这个主题的帖子也值得一看。

第八章:探索持续配置自动化

到目前为止,我们一直在使用单个 VM,手动部署和配置它们。这对实验室和非常小的环境很好,但是如果您必须管理更大的环境,这是一项非常耗时甚至令人厌倦的工作。犯错误和遗漏事项也非常容易,例如 VM 之间的细微差异,更不用说相关的稳定性和安全风险了。例如,在部署过程中选择错误的版本将导致一致性问题,以后进行升级是一个繁琐的过程。

自动化部署和配置管理是缓解这项乏味任务的理想方式。然而,过一段时间,您可能会注意到这种方法存在一些问题。存在许多原因导致问题,以下是一些失败的原因:

  • 脚本失败是因为有些东西发生了变化,例如软件更新引起的。

  • 有一个稍有不同的基础镜像的新版本。

  • 脚本可能很难阅读,难以维护。

  • 脚本依赖于其他组件;例如,操作系统、脚本语言和可用的内部和外部命令。

  • 总有那么一个同事——脚本对你有效,但是,由于某种原因,当他们执行时总是失败。

当然,随着时间的推移,事情已经有所改善:

  • 许多脚本语言现在是多平台的,例如 Bash、Python 和 PowerShell。它们在 Windows、macOS 和 Linux 上都可用。

  • systemd中,带有-H参数的systemctl实用程序可以远程执行命令,即使远程主机是另一个 Linux 发行版也可以执行。更新的systemd版本具有更多功能。

  • firewalldsystemd使用易于部署的配置文件和覆盖。

自动化很可能不是您部署、安装、配置和管理工作负载的答案。幸运的是,还有另一种方法:编排。

从音乐角度来看,编排是研究如何为管弦乐队谱写音乐的学问。您必须了解每种乐器,并知道它们可以发出什么声音。然后,您可以开始谱写音乐;为此,您必须了解乐器如何共同发声。大多数情况下,您从单个乐器开始,例如钢琴。之后,您可以扩展到包括其他乐器。希望结果将是一部杰作,管弦乐队的成员将能够开始演奏。成员如何开始并不重要,但最终指挥确保结果重要。

在计算中有许多与编排相似的地方。在开始之前,您必须了解所有组件的工作原理,它们如何配合以及组件的功能,以便完成工作。之后,您可以开始编写代码以实现最终目标:一个可管理的环境。

云环境最大的优势之一是环境的每个组件都是以软件编写的。是的,我们知道,在最后一行,仍然有许多硬件组件的数据中心,但作为云用户,您不必关心这一点。您需要的一切都是以软件编写的,并且具有 API 进行通信。因此,不仅可以自动化部署 Linux 工作负载,还可以自动化和编排 Linux 操作系统的配置以及应用程序的安装和配置,并保持一切更新。您还可以使用编排工具配置 Azure 资源,甚至可以使用这些工具创建 Linux VM。

在编排中,有两种不同的方法:

  • 命令式:告诉编排工具如何达到这个目标

  • 声明性:告诉编排工具您要实现的目标是什么

一些编排工具可以同时执行这两种方法,但总的来说,在云环境中,声明性方法是更好的方法,因为您有很多选项可以配置,并且可以声明每个选项并实现确切的目标。好消息是,如果这种方法变得太复杂,例如,当编排工具无法理解目标时,您总是可以使用一点命令方法来扩展这种方法,使用脚本。

本章的重点是 Ansible,但我们还将涵盖 PowerShell 期望状态配置DSC)和 Terraform 作为声明性实现的示例。本章的重点是理解编排并了解足够的知识以开始。当然,我们还将讨论与 Azure 的集成。

本章的主要要点是:

  • 了解诸如 Ansible 和 Terraform 等第三方自动化工具以及它们在 Azure 中的使用方式。

  • 使用 Azure 的本地自动化和 PowerShell DSC 来实现机器的期望状态。

  • 如何在 Linux 虚拟机中实现 Azure 策略客户端配置并审计设置。

  • 概述市场上其他可用的自动化部署和配置解决方案。

技术要求

在实践中,您至少需要一个虚拟机作为控制机,或者您可以使用运行 Linux 或 Windows 子系统WSL)的工作站。除此之外,我们还需要一个节点,该节点需要是 Azure 虚拟机。但是,为了提供更好的解释,我们部署了三个节点。如果您的 Azure 订阅受到预算限制,请随时继续使用一个节点。您使用的 Linux 发行版并不重要。本节中的示例用于编排节点,是针对 Ubuntu 节点的,但很容易将其转换为其他发行版。

在本章中,将探讨多种编排工具。对于每个工具,您需要一个干净的环境。因此,在本章中完成 Ansible 部分后,进入 Terraform 之前,请删除虚拟机并部署新的虚拟机。

理解配置管理

在本章的介绍中,您可能已经读到了术语配置管理。让我们更深入地了解一下。配置管理是指您希望如何配置虚拟机。例如,您希望在 Linux 虚拟机中配置 Apache Web 服务器以托管网站;因此,虚拟机的配置部分涉及:

  • 安装 Apache 软件包和依赖项

  • 为 HTTP 流量或 HTTPS 流量打开防火墙端口(如果使用 SSL(安全套接字层)证书)

  • 启用服务并引导它,以便 Apache 服务在启动时启动

这个例子是一个非常简单的 Web 服务器。想象一下一个复杂的场景,您有一个前端 Web 服务器和后端数据库,因此涉及的配置非常复杂。到目前为止,我们一直在谈论单个虚拟机;如果您想要具有相同配置的多个虚拟机怎么办?我们又回到了起点,您必须多次重复配置,这是一项耗时且乏味的任务。这就是编排的作用,正如我们在介绍中讨论的那样。我们可以利用编排工具部署我们想要的虚拟机状态。这些工具将负责配置。此外,在 Azure 中,我们有 Azure 策略客户端配置,可用于审计设置。使用此策略,我们可以定义虚拟机应该处于的条件。如果评估失败或条件未满足,Azure 将标记此计算机为不符合规定。

本章的重点是 Ansible,但我们还将涵盖 PowerShell DSC 和 Terraform 作为声明性实现的示例。本章的重点是理解编排并学习足够的知识以开始。当然,我们还将讨论与 Azure 的集成。

使用 Ansible

Ansible 在本质上是最小的,几乎没有依赖性,并且不会向节点部署代理。对于 Ansible,只需要 OpenSSH 和 Python。它也非常可靠:可以多次应用更改而不会改变初始应用的结果,并且不应该对系统的其余部分产生任何副作用(除非您编写了非常糟糕的代码)。它非常注重代码的重用,这使得它更加可靠。

Ansible 的学习曲线并不是很陡峭。您可以从几行代码开始,然后逐渐扩展,而不会破坏任何东西。在我们看来,如果您想尝试一个编排工具,可以从 Ansible 开始,如果您想尝试另一个编排工具,学习曲线会陡峭得多。

安装 Ansible

在 Azure Marketplace 中,有一个可用于 Ansible 的即用型 VM。目前 Azure Marketplace 中有三个版本的 Ansible:Ansible 实例、Ansible Tower 和 AWX,这是 Ansible Tower 的社区版。在本书中,我们将集中讨论这个免费提供的社区项目;这已经足够学习和开始使用 Ansible 了。之后,您可以转到 Ansible 网站,探索差异,下载企业版的试用版 Ansible,并决定是否需要企业版。

安装 Ansible 有多种方法:

pip install ansible[azure]

Red Hat 和 CentOS 的标准存储库中没有 Python 的pip可用于安装。您必须使用额外的 EPEL 存储库:

sudo yum install epel-release
sudo yum install python-pip

安装完 Ansible 后,检查版本:

ansible --version

如果您不想安装 Ansible,则无需安装:Azure Cloud Shell 中预安装了 Ansible。在撰写本书时,Cloud Shell 支持 Ansible 版本 2.9.0。但是,为了介绍安装过程,我们将选择在 VM 上本地安装 Ansible。要与 Azure 集成,您还需要安装 Azure CLI 以获取您需要提供给 Ansible 的信息。

SSH 配置

您安装 Ansible 的机器现在被称为 ansible-master,换句话说,它只是一个带有 Ansible、Ansible 配置文件和编排指令的虚拟机。与节点的通信使用通信协议进行。对于 Linux,SSH 被用作通信协议。为了使 Ansible 能够以安全的方式与节点通信,使用基于密钥的身份验证。如果尚未完成此操作,请生成一个 SSH 密钥对并将密钥复制到要编排的虚拟机。

要生成 SSH 密钥,请使用此命令:

ssh-keygen

一旦生成密钥,默认情况下,它将保存在用户的主目录中的.ssh目录中。要显示密钥,请使用此命令:

cat ~/.ssh/id_rsa.pub

一旦我们有了密钥,我们必须将该值复制到节点服务器。按照以下步骤复制密钥:

  1. 复制id_rsa.pub文件的内容。

  2. SSH 到您的节点服务器。

  3. 使用sudo命令切换到超级用户。

  4. 编辑~/.ssh/中的authorized_keys文件。

  5. 粘贴我们从 Ansible 服务器复制的密钥。

  6. 保存并关闭文件。

要验证过程是否成功,请返回到安装了 Ansible 的机器(从现在开始,我们将称其为 ansible-master)并ssh到节点。如果在生成密钥时使用了密码,它将要求输入密码。自动复制密钥的另一种方法是使用ssh-copy-id命令。

最低限度的配置

配置 Ansible,您将需要一个ansible.cfg文件。有不同的位置可以存储这个配置文件,Ansible 按以下顺序搜索:

ANSIBLE_CONFIG (environment variable if set)
ansible.cfg (in the current directory)
~/.ansible.cfg (in the home directory)
/etc/ansible/ansible.cfg

Ansible 将处理前面的列表,并使用找到的第一个文件;其他所有文件都将被忽略。

如果不存在,请在/etc中创建ansible目录,并添加一个名为ansible.cfg的文件。这是我们将保存配置的地方:

[defaults]
inventory = /etc/ansible/hosts

让我们试试以下:

ansible all -a "systemctl status sshd"

这个命令,称为临时命令,对/etc/ansiblehosts中定义的所有主机执行systemctl status sshd。如果每个主机有多个用户名,你也可以在以下 ansible 主机文件中指定这些节点的用户名:

<ip address>   ansible_ssh_user='<ansible user>'

因此,如果需要,你可以像以下截图中所示将用户添加到清单文件行项目,并且对于三个节点,文件将如下所示:

代码将用户添加到清单文件行项目

图 8.1:将用户添加到清单文件行项目

再试一次。使用远程用户而不是本地用户名。现在你可以登录并执行命令了。

清单文件

Ansible 清单文件定义了主机和主机组。基于此,你可以调用主机或组(一组主机)并运行特定的 playbook 或执行命令。

在这里,我们将调用我们的组nodepool并添加我们节点的 IP。由于我们所有的 VM 都在同一个 Azure VNet 中,我们使用私有 IP。如果它们在不同的网络中,你可以添加公共 IP。在这里,我们使用三个 VM 来帮助解释。如果你只有一个节点,只需输入那一个。

你也可以使用 VM 的 DNS 名称,但它们应该添加到你的/etc/hosts文件中以进行解析:

[nodepool] 
10.0.0.5 
10.0.0.6
10.0.0.7

另一个有用的参数是ansible_ssh_user。你可以使用它来指定用于登录节点的用户名。如果你在 VMs 上使用多个用户名,这种情况就会出现。

在我们的示例中,不要使用all,你可以使用一个名为ansible-nodes的组名。还可以使用通用变量,这些变量对每个主机都有效,并且可以针对每个服务器进行覆盖;例如:

[all:vars]
ansible_ssh_user='student'
[nodepool]
<ip address> ansible_ssh_user='other user'

有时,你需要特权来执行命令:

ansible nodepool-a "systemctl restart sshd"

这会得到以下错误消息:

Failed to restart sshd.service: Interactive authentication required.
See system logs and 'systemctl status sshd.service' for details.non-zero return code.

对于临时命令,只需将-b选项作为 Ansible 参数添加,以启用特权升级。它将默认使用sudo方法。在 Azure 镜像中,如果你使用sudo,就不需要提供 root 密码。这就是为什么-b选项可以无问题地工作。如果你配置了sudo来提示输入密码,使用-K

我们建议运行其他命令,比如netstatping,以了解这些命令在这些机器上是如何执行的。运行netstat并使用sshd进行 grep 将会得到类似于这样的输出:

netstat 和 grep ssh 命令的输出

图 8.2:运行 netstat 并使用 sshd 进行 grep

注意

当运行ansible all命令时,你可能会收到弃用警告。为了抑制这个警告,在ansible.cfg中使用deprecation_warnings=False

Ansible Playbooks 和 Modules

使用临时命令是一种命令方法,不比只使用 SSH 客户端远程执行命令更好。

有两个组件,你需要将其变成真正的命令编排:一个 playbook 和一个模块。Playbook 是部署、配置和维护系统的基础。它可以编排一切,甚至在主机之间!Playbook 用于描述你想要达到的状态。Playbook 是用 YAML 编写的,可以用ansible-playbook命令执行:

ansible-playbook <filename>

第二个组件是模块。描述模块的最佳方式是:执行任务以达到期望的状态。它们也被称为任务插件或库插件。

所有可用的模块都有文档;你可以在网上和你的系统上找到文档。

要列出所有可用的插件文档,请执行以下命令:

ansible-doc -l

这会花一些时间。我们建议你将结果重定向到一个文件。这样,它会花费更少的时间,而且更容易搜索模块。

例如,让我们尝试创建一个 playbook,如果用户尚不存在,则使用user模块创建用户。换句话说,期望的状态是存在特定用户。

首先阅读文档:

ansible-doc user

在 Ansible 目录中创建一个文件,例如playbook1.yaml,内容如下。验证用户文档中的参数:

---
- hosts: all
  tasks:
  - name: Add user Jane Roe
    become: yes
    become_method: sudo
    user:
      state: present
      name: jane
      create_home: yes
      comment: Jane Roe
      generate_ssh_key: yes
      group: users
      groups:
        - sudo
        - adm
      shell: /bin/bash
      skeleton: /etc/skel

从输出中,您可以看到所有主机返回了OK,并且用户已创建:

创建的 ansible 文件的参数

图 8.3:运行 Ansible playbook

为了确保用户已创建,我们将检查所有主机上的/etc/passwd文件。从输出中,我们可以看到用户已经被创建:

检查主机的密码文件以验证用户创建

图 8.4:使用/etc/passwd 验证用户创建

确保缩进正确,因为 YAML 在缩进和空格方面非常严格。使用支持 YAML 的编辑器,如 vi、Emacs 或 Visual Studio Code,确实有很大帮助。

如果需要运行命令特权提升,可以使用becomebecome_method-b

要检查 Ansible 语法,请使用以下命令:

ansible-playbook --syntax-check Ansible/example1.yaml

让我们继续看看如何在 Azure 进行身份验证并开始部署。

身份验证到 Microsoft Azure

要将 Ansible 与 Microsoft Azure 集成,您需要创建一个配置文件,为 Ansible 提供 Azure 的凭据。

凭据必须存储在您的主目录中的~/.azure/credentials文件中。首先,我们必须使用 Azure CLI 收集必要的信息。如下身份验证到 Azure:

az login

如果您成功登录,您将获得类似以下的输出:

用户凭据指示成功的 Azure 登录

图 8.5:使用 az 登录命令登录到 Azure

这已经是您需要的信息的一部分。如果您已经登录,请执行以下命令:

az account list

创建服务主体:

az ad sp create-for-rbac --name <principal> --password <password>

应用 ID 是您的client_id,密码是您的secret,将在我们即将创建的凭据文件中引用。

创建~/.azure/credentials文件,内容如下:

[default]
subscription_id=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 
client_id=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 
secret=xxxxxxxxxxxxxxxxx 
tenant=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 

使用ansible-doc -l | grep azure查找可用于 Azure 的 Ansible 模块。将内容重定向到文件以供参考。

资源组

让我们检查一切是否如预期那样工作。创建一个名为resourcegroup.yaml的新 playbook,内容如下:

---
- hosts: localhost
  tasks:
  - name: Create a resource group
    azure_rm_resourcegroup:
      name: Ansible-Group
      location: westus

请注意,主机指令是 localhost!执行 playbook 并验证资源组是否已创建:

az group show --name Ansible-Group

输出应该与以下非常相似:

{
 "id": "/subscriptions/xxxx/resourceGroups/Ansible-Group",
 "location": "westus",
 "managedBy": null,
 "name": "Ansible-Group",
 "properties": {
 "provisioningState": "Succeeded"
 },
 "tags": null
 }

虚拟机

让我们使用 Ansible 在 Azure 中创建一个虚拟机。为此,请创建一个virtualmachine.yaml文件,内容如下。检查每个块的name字段,以了解代码的作用:

- hosts: localhost
  tasks:
  - name: Create Storage Account
    azure_rm_storageaccount:
      resource_group: Ansible-Group
      name: ansiblegroupsa
      account_type: Standard_LRS
	 .
	 .
	 .	  - name: Create a CentOS VM
    azure_rm_virtualmachine:
      resource_group: Ansible-Group
      name: ansible-vm
      vm_size: Standard_DS1_v2
      admin_username: student
  admin_password:welk0mITG!
      image:
        offer: CentOS
        publisher: OpenLogic
        sku: '7.5'
        version: latest

考虑到代码的长度,我们只在这里展示了一小部分。您可以从本书的 GitHub 存储库中的chapter 8文件夹中下载整个virtualmachine.yaml文件。

在下面的截图中,您可以看到 Ansible 创建了 VM 所需的所有资源:

使用 Ansible 创建 VM 所需的所有资源

图 8.6:使用 Ansible 创建 VM 所需的所有资源

您可以在 Ansible 的 Microsoft Azure 指南中找到使用 Ansible 部署 Azure VM 的完整示例(docs.ansible.com/ansible/latest/scenario_guides/guide_azure.html)。

Ansible 中的 Azure 清单管理

我们已经学会了两种在 Azure 中使用 Ansible 的方法:

  • 在清单文件中使用 Ansible 连接到 Linux 机器。实际上,无论是在 Azure 中还是在其他地方运行都无关紧要。

  • 使用 Ansible 管理 Azure 资源。

在本节中,我们将进一步进行。我们将使用动态清单脚本询问 Azure 在您的环境中运行了什么,而不是使用静态清单。

第一步是下载 Azure 的动态清单脚本。如果您不是 root 用户,请使用sudo执行:

cd /etc/ansible
wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/azure_rm.py
chmod +x /etc/ansible/azure_rm.py

编辑/etc/ansible/ansible.cfg文件并删除inventory=/etc/ansible/hosts行。

让我们进行第一步:

ansible -i /etc/ansible/azure_rm.py azure -m ping

它可能会因为身份验证问题而失败:

由于身份验证问题导致主机连接失败

图 8.7:由于身份验证问题导致主机连接失败

如果您对不同的 VM 有不同的登录,您可以始终在每个任务中使用用户指令。在这里,我们使用azure,这意味着所有 VM。您始终可以使用 VM 名称查询机器。例如,您可以使用用户凭据 pingansible-node3 VM:

使用用户凭据 ping ansible-node3 VM

图 8.8:查询 ansible-node3 VM

理想情况下,Ansible 希望您使用 SSH 密钥而不是密码。如果您想使用密码,可以使用–extra-vars并传递密码。请注意,为此您需要安装一个名为sshpass的应用程序。要通过 Ansible ping 使用密码的 Azure VM,请执行以下命令:

ansible -i azure_rm.py ansible-vm -m ping \
--extra-vars "ansible_user=<username> ansible_password=<password>"

让我们以前一个示例中使用 Ansible 创建的 VM 实例为例,其中用户名是student,密码是welk0mITG!。从屏幕截图中,您可以看到 ping 成功。您可能会看到一些警告,但可以安全地忽略它们。但是,如果 ping 失败,则需要进一步调查:

屏幕截图表明对用户的 ping 成功

图 8.9:发送 ping 给用户名 student

通过在与azure_rm.py目录相同的目录中创建一个azure_rm.ini文件,您可以修改清单脚本的行为。以下是一个示例ini文件:

[azure]
include_powerstate=yes
group_by_resource_group=yes
group_by_location=yes
group_by_security_group=yes
group_by_tag=yes

它的工作方式与hosts文件非常相似。[azure]部分表示所有 VM。您还可以为以下内容提供部分:

  • 位置名称

  • 资源组名称

  • 安全组名称

  • 标记键

  • 标记键值

选择一个或多个 VM 的另一种方法是使用标记。要能够给 VM 打标记,您需要 ID:

az vm list --output tsv

现在,您可以给 VM 打标记:

az resource tag --resource-group <resource group> \
  --tags webserver --id </subscriptions/...>

您还可以在 Azure 门户中给 VM 打标记:

在 Azure 门户中给 VM 打标记

图 8.10:在 Azure 门户中给 VM 打标记

单击更改并添加一个标记,带有或不带有值(您也可以使用该值来过滤该值)。要验证,请使用标记名称主机:

ansible -i /etc/ansible/azure_rm.py webserver -m ping

只有标记的 VM 被 ping。让我们为这个标记的 VM 创建一个 playbook,例如/etc/ansible/example9.yaml。标记再次在hosts指令中使用:

---
- hosts: webserver
  tasks:
  - name: Install Apache Web Server
    become: yes
    become_method: sudo
    apt:
      name: apache2
      install_recommends: yes
      state: present
      update-cache: yes
    when:
      - ansible_distribution == "Ubuntu"
      - ansible_distribution_version == "18.04"

执行 playbook:

ansible-playbook -i /etc/ansible/azure_rm.py /etc/ansible/example9.yaml

一旦 playbook 运行完毕,如果您检查 VM,您会看到 Apache 已安装。

如前所述,Ansible 并不是唯一的工具。还有另一个流行的工具叫做 Terraform。在下一节中,我们将讨论 Azure 上的 Terraform。

使用 Terraform

Terraform 是由 HashiCorp 开发的另一个基础设施即代码IaC)工具。您可能想知道为什么它被称为 IaC 工具。原因是您可以使用代码定义基础设施的需求,而 Terraform 将帮助您部署它。Terraform 使用HashiCorp 配置语言HCL);但是,您也可以使用 JSON。Terraform 在 macOS、Linux 和 Windows 中都受支持。

Terraform 支持各种 Azure 资源,如网络、子网、存储、标记和 VM。如果您回忆一下,我们讨论了编写代码的命令式和声明式方式。Terraform 是声明式的,它可以维护基础设施的状态。一旦部署,Terraform 会记住基础设施的当前状态。

与每个部分一样,过程的第一部分涉及安装 Terraform。让我们继续进行 Terraform 的 Linux 安装。

安装

Terraform 的核心可执行文件可以从www.terraform.io/downloads.html下载,并且可以复制到添加到您的$PATH变量的目录之一。您还可以使用wget命令来下载核心可执行文件。要做到这一点,首先您必须从上述链接中找出 Terraform 的最新版本。在撰写本文时,最新版本为 0.12.16

现在我们已经有了版本,我们将使用以下命令使用wget下载可执行文件:

wget https://releases.hashicorp.com/terraform/0.12.17/terraform_0.12.17_linux_amd64.zip

ZIP 文件将被下载到当前工作目录。现在我们将使用解压工具来提取可执行文件:

unzip terraform_0.12.16_linux_amd64.zip

注意

unzip可能不是默认安装的。如果它抛出错误,请使用aptyum根据您使用的发行版进行安装unzip

提取过程将为您获取 Terraform 可执行文件,您可以将其复制到$PATH中的任何位置。

要验证安装是否成功,您可以执行:

terraform --version

现在我们已经确认了 Terraform 已经安装,让我们继续设置 Azure 的身份验证。

连接到 Azure

有多种方法可以用来对 Azure 进行身份验证。您可以使用 Azure CLI、使用客户端证书的服务主体、服务主体和客户端密钥,以及许多其他方法。对于测试目的,使用az登录命令的 Azure CLI 是正确的选择。然而,如果我们想要自动化部署,这并不是一个理想的方法。我们应该选择服务主体和客户端密钥,就像我们在 Ansible 中所做的那样。

让我们从为 Terraform 创建一个服务主体开始。如果您已经为上一节创建了服务主体,请随意使用。要从 Azure CLI 创建一个新的服务主体,请使用以下命令:

az ad sp create-for-rbac -n terraform

此时,您可能已经熟悉了输出,其中包含appID、密码和租户 ID。

记下输出中的值,我们将创建变量来存储这个值:

export ARM_CLIENT_ID="<appID>"
export ARM_CLIENT_SECRET="<password>"
export ARM_SUBSCRIPTION_ID="<subscription ID>"
export ARM_TENANT_ID="<tenant ID>"

因此,我们已经将所有的值存储到变量中,这些值将被 Terraform 用于身份验证。由于我们已经处理了身份验证,让我们用 HCL 编写代码,以便在 Azure 中部署资源。

部署到 Azure

您可以使用任何代码编辑器来完成这个任务。由于我们已经在 Linux 机器上,您可以使用 vi 或 nano。如果您愿意,您也可以使用 Visual Studio Code,它具有 Terraform 和 Azure 的扩展,可以为您提供智能感知和语法高亮显示。

让我们创建一个 terraform 目录来存储我们所有的代码,在terraform目录中,我们将根据我们要部署的内容创建进一步的目录。在我们的第一个示例中,我们将使用 Terraform 在 Azure 中创建一个资源组。稍后,我们将讨论如何在这个资源组中部署一个 VM。

因此,要创建一个terraform目录,并在此目录中创建一个resource-group子文件夹,请执行以下命令:

mkdir terraform
cd terraform && mkdir resource-group
cd resource-group

接下来,创建一个包含以下内容的 main.tf 文件:

provider "azurerm" {
    version = "~>1.33"
}
resource "azurerm_resource_group" "rg" {
    name     = "TerraformOnAzure"
    location = "eastus"
}

代码非常简单。让我们仔细看看每个项目。

提供者指令显示我们想要使用azurerm提供者的 1.33 版本。换句话说,我们指示我们将使用 Terraform Azure 资源管理器提供者的 1.33 版本,这是 Terraform 可用的插件之一。

resource指令表示我们将部署一个azurerm_resource_group类型的 Azure 资源,具有namelocation两个参数。

rg代表资源配置。每个模块中的资源名称必须是唯一的。例如,如果您想在同一个模板中创建另一个资源组,您不能再次使用rg,因为您已经使用过了;而是,您可以选择除rg之外的任何其他名称,比如rg2

在使用模板开始部署之前,我们首先需要初始化项目目录,即我们的resource-group文件夹。要初始化 Terraform,请执行以下操作:

terraform init

在初始化期间,Terraform 将从其存储库下载azurerm提供程序,并显示类似以下的输出:

Terraform 从其存储库下载 azurerm 提供程序

图 8.11:初始化 Terraform 以下载 azurerm 提供程序

由于我们已经将服务主体详细信息导出到变量中,因此可以使用此命令进行部署:

terraform apply

此命令将连接 Terraform 到您的 Azure 订阅,并检查资源是否存在。如果 Terraform 发现资源不存在,它将继续创建一个执行计划以部署。您将获得以下截图中显示的输出。要继续部署,请键入yes

使用 terraform apply 连接 terraform 到 Azure 订阅

图 8.12:连接 Terraform 到 Azure 订阅

一旦您提供了输入,Terraform 将开始创建资源。创建后,Terraform 将向您显示创建的所有内容的摘要以及添加和销毁的资源数量,如下所示:

使用 terraform apply 命令创建的资源摘要

图 8.13:创建的资源摘要

在我们初始化 Terraform 的项目目录中将生成一个名为terraform.tfstate的状态文件。该文件将包含状态信息,还将包含我们部署到 Azure 的资源列表。

我们已成功创建了资源组;在下一节中,我们将讨论如何使用 Terraform 创建 Linux VM。

部署虚拟机

在前面的示例中,我们创建了资源组,我们使用azurerm_resource_group作为要创建的资源。对于每个资源,都会有一个指令,例如对于 VM,它将是azurerm_virtual_machine

此外,我们使用terraform apply命令创建了资源组。但是 Terraform 还提供了一种使用执行计划的方法。因此,我们可以先创建一个计划,看看将会做出什么更改,然后再部署。

首先,您可以返回到terraform目录并创建一个名为vm的新目录。为不同的项目单独创建目录总是一个好主意:

mkdir ../vm
cd ../vm

一旦您进入目录,您可以创建一个名为main.tf的新文件,其中包含以下代码块中显示的内容。使用添加的注释查看每个块的目的。考虑到代码的长度,我们显示了代码块的截断版本。您可以在本书的 GitHub 存储库的第八章文件夹中找到main.tf代码文件:

provider "azurerm" {
    version = "~>1.33"
}
#Create resource group
resource "azurerm_resource_group" "rg" {
    name     = "TFonAzure"
    location = "eastus"
}
.
.
.
#Create virtual machine, combining all the components we created so far
resource "azurerm_virtual_machine" "myterraformvm" {
    name                  = "tf-VM"
    location              = "eastus"
    resource_group_name   = azurerm_resource_group.rg.name
    network_interface_ids = [azurerm_network_interface.nic.id]
    vm_size               = "Standard_DS1_v2"
    storage_os_disk {
        name              = "tfOsDisk"
        caching           = "ReadWrite"
        create_option     = "FromImage"
        managed_disk_type = "Standard_LRS"
    }
    storage_image_reference {
        publisher = "Canonical"
        offer     = "UbuntuServer"
        sku       = "16.04.0-LTS"
        version   = "latest"
    }
    os_profile {
        computer_name  = "tfvm"
        admin_username = "adminuser"
        admin_password = "Pa55w0rD!@1234"
    }

    os_profile_linux_config {
    disable_password_authentication = false
  }
}

如果您查看azurerm_virtual_network部分,您会发现,我们没有写下资源名称,而是以type.resource_configuration.parameter的格式给出了一个引用。在这种情况下,我们没有写下资源组名称,而是给出了azurerm_resource_group.rg.name的引用。同样,在整个代码中,我们都采用了引用以使部署变得更加容易。

在开始部署规划之前,我们必须使用以下内容初始化项目:

terraform init

如前所述,我们将使用执行计划。要创建执行计划并将其保存到vm-plan.plan文件中,请执行:

terraform plan -out vm-plan.plan

您将收到很多警告;它们可以安全地忽略。确保代码没有显示任何错误。如果成功创建了执行计划,它将显示执行计划的下一步操作,如下所示:

成功创建执行计划

图 8.14:显示执行计划

如输出所建议的,我们将执行:

terraform apply "vm-plan.plan"

现在,部署将开始,并显示正在部署的资源,已经经过了多少时间等等,如输出所示:

部署资源的详细信息

图 8.15:资源部署详细信息

最后,Terraform 将提供部署的资源数量的摘要:

部署资源的摘要

图 8.16:部署的资源数量摘要

还有另一个命令,就是show命令。这将显示部署的完整状态,如下截图所示:

检查部署的完整状态

图 8.17:显示部署的完整状态

我们编写了一小段代码,可以在 Azure 中部署一个 VM。但是,可以通过向代码添加许多参数来进行高级状态配置。所有参数的完整列表都可以在 Terraform 文档(www.terraform.io/docs/providers/azurerm/r/virtual_machine.html)和微软文档(docs.microsoft.com/en-us/azure/virtual-machines/linux/terraform-create-complete-vm)中找到。

由于这些模板有点高级,它们将使用变量而不是重复的值。然而,一旦您习惯了这一点,您就会了解 Terraform 有多强大。

最后,您可以通过执行以下命令销毁整个部署或项目:

terraform destroy

这将删除项目的main.tf文件中提到的所有资源。如果您有多个项目,您必须导航到项目目录并执行destroy命令。执行此命令时,系统将要求您确认删除;一旦您说“是”,资源将被删除:

使用 terraform destroy 命令销毁整个部署

图 8.18:使用 terraform destroy 命令删除所有资源

最后,您将得到一个摘要,如下所示:

被销毁资源的摘要

图 8.19:被销毁资源的摘要

现在我们熟悉了在 Azure 上使用 Terraform 和部署简单的 VM。如今,由于 DevOps 的可用性和采用率,Terraform 正变得越来越受欢迎。 Terraform 使评估基础架构并重新构建它变得轻松无忧。

使用 PowerShell DSC

与 Bash 一样,PowerShell 是一个具有强大脚本功能的 shell。我们可能会认为 PowerShell 更像是一种脚本语言,可以用来执行简单的操作或创建资源,就像我们迄今为止所做的那样。但是,PowerShell 的功能远不止于此,还延伸到自动化和配置。

DSC 是 PowerShell 的一个重要但鲜为人知的部分,它不是用 PowerShell 语言自动化脚本,而是提供了 PowerShell 中的声明性编排。

如果将其与 Ansible 进行比较,对 Linux 的支持非常有限。但它非常适用于常见的管理任务,缺少的功能可以通过 PowerShell 脚本来补偿。微软非常专注于使其与 Windows Server 保持一致。一旦发生这种情况,它将被 PowerShell DSC Core 取代,这与他们以前使用 PowerShell | PowerShell Core 所做的非常相似。这将在 2019 年底完成。

另一个重要的注意事项是,由于某种原因,随 DSC 提供的 Python 脚本偶尔无法正常工作,有时会出现 401 错误甚至未定义的错误。首先确保您拥有最新版本的 OMI 服务器和 DSC,然后再试一次;有时,您可能需要尝试两到三次。

Azure Automation DSC

使用 DSC 的一种方法是使用 Azure Automation DSC。这样,您就不必使用单独的机器作为控制节点。要使用 Azure Automation DSC,您需要一个 Azure Automation 帐户。

自动化帐户

在 Azure 门户中,选择左侧栏中的所有服务,导航到管理+治理,然后选择自动化帐户。创建一个自动化帐户,并确保选择Run As Account

再次导航到所有服务管理工具,然后选择刚创建的帐户

选择新创建的自动化帐户

图 8.20:在 Azure 门户中创建自动化帐户

在这里,您可以管理您的节点、配置等等。

请注意,此服务并非完全免费。流程自动化按作业执行分钟计费,而配置管理按受管节点计费。

要使用此帐户,您需要Run As Account的注册 URL 和相应的密钥。这两个值都可以在帐户密钥设置下找到。

或者,在 PowerShell 中,执行以下命令:

Get-AzAutomationRegistrationInfo ' 
  -ResourceGroup <resource group> '
  -AutomationAccountName <automation account name>

Linux 有一个可用的 VM 扩展;这样,您可以部署 VM,包括它们的配置,完全编排。

有关更多信息,请访问github.com/Azure/azure-linux-extensions/tree/master/DSCdocs.microsoft.com/en-us/azure/virtual-machines/extensions/dsc-linux

因为我们要使用 Linux 和 DSC,我们需要一个名为nx的 DSC 模块。该模块包含了 Linux 的 DSC 资源。在自动化帐户的设置中,选择nx并导入该模块。

在 Linux 上安装 PowerShell DSC

要在 Linux 上使用 PowerShell DSC,您需要 Open Management Infrastructure Service。支持的 Linux 发行版版本如下:

  • Ubuntu 12.04 LTS,14.04 LTS 和 16.04 LTS。目前不支持 Ubuntu 18.04。

  • RHEL/CentOS 6.5 及更高版本。

  • openSUSE 13.1 及更高版本。

  • SUSE Linux Enterprise Server 11 SP3 及更高版本。

该软件可在github.com/Microsoft/omi下载。

在基于 Red Hat 的发行版上的安装如下:

sudo yum install \
  https://github.com/Microsoft/omi/releases/download/\
  v1.4.2-3/omi-1.4.2-3.ssl_100.ulinux.x64.rpm

对于 Ubuntu,您可以使用wget从 GitHub 存储库下载deb文件,并使用dpkg进行安装:

dpkg –i ./omi-1.6.0-0.ssl_110.ulinux.x64.deb 

注意

确保下载与您的 SSL 版本匹配的文件。您可以使用openssl version命令来检查您的 SSL 版本。

安装后,服务会自动启动。使用以下命令检查服务的状态:

sudo systemctl status omid.service

要显示产品和版本信息,包括使用的配置目录,请使用以下命令:

/opt/omi/bin/omicli id

带有配置目录列表的产品信息

图 8.21:显示产品和版本信息

创建期望状态

PowerShell DSC 不仅仅是一个带有参数的脚本或代码,就像在 Ansible 中一样。要开始使用 PowerShell DSC,您需要一个必须编译成管理对象格式MOF)文件的配置文件。

但是,首先要做的是。让我们创建一个名为example1.ps1的文件,内容如下:

Configuration webserver {
Import-DscResource -ModuleName PSDesiredStateConfiguration,nx
Node "ubuntu01"{
    nxPackage apache2
    {
        Name = "apache2"
        Ensure = "Present"
        PackageManager = "apt"
    }
 }
}
webserver

让我们调查一下这个配置。正如所述,它与函数声明非常相似。配置得到一个标签,并在脚本末尾执行。必要的模块被导入,VM 的主机名被声明,配置开始。

PowerShell DSC 资源

在这个配置文件中,使用了一个名为nxPackage的资源。有几个内置资源:

  • nxArchive:提供在特定路径解压归档(.tar.zip)文件的机制。

  • nxEnvironment:管理环境变量。

  • nxFile:管理文件和目录。

  • nxFileLine:管理 Linux 文件中的行。

  • nxGroup:管理本地 Linux 组。

  • nxPackage:管理 Linux 节点上的软件包。

  • nxScript:运行脚本。大多数情况下,这是用来暂时切换到更命令式的编排方法。

  • nxService:管理 Linux 服务(守护进程)。

  • nxUser:管理 Linux 用户。

您还可以使用 MOF 语言、C#、Python 或 C/C++编写自己的资源。

您可以访问docs.microsoft.com/en-us/powershell/dsc/lnxbuiltinresources来使用官方文档。

保存脚本并执行如下:

pwsh -file example1.ps

脚本的结果是创建一个与配置名称相同的目录。其中,有一个以 MOF 格式的 localhost 文件。这是用于描述 CIM 类的语言(CIM代表通用信息模型)。CIM 是用于管理完整环境(包括硬件)的开放标准。

我们认为这个描述足以理解为什么微软选择了这个模型和相应的编排语言文件!

您还可以将配置文件上传到 Azure,在DSC Configurations下。按下Compile按钮在 Azure 中生成 MOF 文件。

在 Azure 中应用资源

如果需要,您可以使用/opt/microsoft/dsc/Scripts中的脚本在本地应用所需的状态,但在我们看来,这并不像应该那样容易。而且,因为本章是关于 Azure 中的编排,我们将直接转向 Azure。

注册虚拟机:

sudo /opt/microsoft/dsc/Scripts/Register.py \
  --RegistrationKey <automation account key> \
  --ConfigurationMode ApplyOnly \
  --RefreshMode Push --ServerURL <automation account url>

再次检查配置:

sudo /opt/microsoft/dsc/Scripts/GetDscLocalConfigurationManager.py

现在,节点在Automation Account设置下的DSC Nodes窗格中可见。现在,您可以链接上传和编译的 DSC 配置。配置已应用!

另一种方法是使用Add Node选项,然后选择 DSC 配置。

总之,PowerShell DSC 的主要用例场景是编写、管理和编译 DSC 配置,以及导入和分配这些配置到云中的目标节点。在使用任何工具之前,您需要了解用例场景以及它们如何适用于您的环境以实现目标。到目前为止,我们一直在配置虚拟机;接下来的部分将介绍如何使用 Azure 策略来审计 Linux 虚拟机内部的设置。

Azure 策略客户端配置

策略主要用于资源的治理。Azure 策略是 Azure 中的一个服务,您可以在其中创建、管理和分配策略。这些策略可用于审计和合规性。例如,如果您在东部美国位置托管一个安全的应用程序,并且希望仅限制在东部美国的部署,可以使用 Azure 策略来实现这一点。

假设您不希望在订阅中部署 SQL 服务器。在 Azure 策略中,您可以创建一个策略并指定允许的服务,只有它们可以在该订阅中部署。请注意,如果您将策略分配给已经存在资源的订阅,Azure 策略只能对分配后创建的资源进行操作。但是,如果任何现有资源在分配前不符合策略,它们将被标记为“不合规”,因此管理员可以在必要时进行更正。此外,Azure 策略只会在部署的验证阶段生效。

一些内置策略包括:

  • 允许的位置:使用此功能,您可以强制执行地理合规性。

  • 允许的虚拟机 SKU:定义一组虚拟机 SKU。

  • 向资源添加标签:向资源添加标签。如果没有传递值,它将采用默认的标签值。

  • 强制执行标签及其值:用于强制执行对资源所需的标签及其值。

  • 不允许的资源类型:防止部署选定的资源。

  • 允许的存储帐户 SKU:我们在上一章中讨论了可用于存储帐户的不同 SKU,如 LRS、GRS、ZRS 和 RA-GRS。您可以指定允许的 SKU,其余的将被拒绝部署。

  • 允许的资源类型:正如我们在示例中提到的,您可以指定订阅中允许的资源。例如,如果您只想要 VM 和网络,您可以接受Microsoft.ComputeMicrosoft.Network资源提供程序;所有其他提供程序都不允许部署。

到目前为止,我们已经讨论了 Azure Policy 如何用于审计 Azure 资源,但它也可以用于审计 VM 内部的设置。Azure Policy 通过使用 Guest Configuration 扩展和客户端来完成这项任务。扩展和客户端共同确认了客户操作系统的配置、应用程序的存在、状态以及客户操作系统的环境设置。

Azure Policy Guest Configuration 只能帮助您审计客户 VM。在撰写本文时,不支持应用配置。

Linux 的 Guest Configuration 扩展

客户策略配置由 Guest Configuration 扩展和代理完成。VM 上的 Guest Configuration 代理通过使用 Linux 的 Guest Configuration 扩展进行配置。正如前面讨论的,它们共同工作,允许用户在 VM 上运行客户策略,从而帮助用户审计 VM 上的策略。Chef InSpec (www.inspec.io/docs/)是 Linux 的客户策略。让我们看看如何将扩展部署到 VM 并使用扩展支持的命令。

部署到虚拟机

要做到这一点,您需要有一个 Linux VM。我们将通过执行以下操作将 Guest Configuration 扩展部署到 VM。

az vm extension set --resource-group <resource-group> \
--vm-name <vm-name> \
--name ConfigurationForLinux \
--publisher Microsoft.GuestConfiguration \
--version 1.9.0

您将获得类似于此的输出:

将 Guest Configuration 扩展部署到 VM

图 8.22:将 Guest Configuration 扩展部署到 VM

命令

Guest Configuration 扩展支持installuninstallenabledisableupdate命令。要执行这些命令,您需要切换当前工作目录到/var/lib/waagent/Microsoft.GuestConfiguration.ConfigurationForLinux-1.9.0/bin。之后,您可以使用guest-configuration-shim脚本链接可用的命令。

注意

检查文件是否启用了执行位。如果没有,使用chmod +x guest-configuration-shim来设置执行权限。

执行任何命令的一般语法是./guest-configuration-shim <command name>

例如,如果您想要安装 Guest Configuration 扩展,可以使用install命令。当扩展已安装时,将调用enable,这将提取代理程序包,安装并启用代理程序。

类似地,update将更新代理服务到新代理,disable禁用代理,最后,uninstall将卸载代理。

代理程序下载到路径,例如/var/lib/waagent/Microsoft.GuestConfiguration.ConfigurationForLinux-<version>/GCAgent/DSC,并且agent输出保存在此目录中的stdoutstderr文件中。如果遇到任何问题,请验证这些文件的内容。尝试理解错误,然后进行故障排除。

日志保存在/var/log/azure/Microsoft.GuestConfiguration.ConfigurationForLinux。您可以使用这些日志来调试问题。

目前,Azure Policy Guest Configuration 支持以下操作系统版本:

Azure Policy Guest Configuration 支持的操作系统版本

图 8.23:Azure Policy Guest Configuration 支持的操作系统版本

Azure 策略以 JSON 清单的形式编写。编写策略不是本书的一部分;您可以参考 Microsoft 共享的示例策略(github.com/MicrosoftDocs/azure-docs/raw/master/articles/governance/policy/samples/guest-configuration-applications-installed-linux.md)。此示例用于审计 Linux VM 中是否安装了特定应用程序。

如果您调查示例,您将了解组件是什么,以及如何在您的上下文中使用参数。

其他解决方案

编排市场中的另一个重要参与者是 Puppet。直到最近,Puppet 在 Azure 中的支持非常有限,但这种情况正在迅速改变。Puppet 模块puppetlabs/azure_arm仍然处于起步阶段,但puppetlabs/azure为您提供了所需的一切。这两个模块都需要 Azure CLI 才能工作。将 Azure CLI 集成到其商业 Puppet Enterprise 产品中的工作非常出色。Azure 有一个 VM 扩展,可用于将成为 Puppet 节点的 VM。

更多信息请访问puppet.com/products/managed-technology/microsoft-windows-azure

您还可以选择 Chef 软件,它提供了一个自动化和编排平台,已经存在很长时间了。它的开发始于 2009 年!用户编写“食谱”,描述 Chef 如何使用诸如刀具之类的工具管理“厨房”。在 Chef 中,许多术语都来自厨房。Chef 与 Azure 集成非常好,特别是如果您从 Azure Marketplace 使用 Chef Automate。还有一个 VM 扩展可用。Chef 适用于大型环境,学习曲线相对陡峭,但至少值得一试。

更多信息请访问www.chef.io/partners/azure/

总结

我们从简要介绍编排、使用编排的原因以及不同的方法:命令式与声明式开始了本章。

之后,我们介绍了 Ansible、Terraform 和 PowerShell DSC 平台。关于以下内容涵盖了许多细节:

  • 如何安装平台

  • 在操作系统级别处理资源

  • 与 Azure 集成

Ansible 是迄今为止最完整的解决方案,也可能是学习曲线最平缓的解决方案。但是,所有解决方案都非常强大,总是有办法克服它们的局限性。对于所有编排平台来说,未来在功能和能力方面都是充满希望的。

在 Azure 中创建 Linux VM 并不是在 Azure 中创建工作负载的唯一方法;您还可以使用容器虚拟化来部署应用程序的平台。在下一章中,我们将介绍容器技术。

问题

在本章中,让我们跳过常规问题。启动一些 VM 并选择您选择的编排平台。配置网络安全组以允许 HTTP 流量。

尝试使用 Ansible、Terraform 或 PowerShell DSC 配置以下资源:

  1. 创建用户并将其设置为wheel组(基于 RH 的发行版)或sudo(Ubuntu)的成员。

  2. 安装 Apache Web 服务器,从/wwwdata提供内容,使用 AppArmor(Ubuntu)或 SELinux(基于 RHEL 的发行版)进行安全保护,并在此 Web 服务器上提供一个漂亮的index.html页面。

  3. 将 SSH 限制为您的 IP 地址。HTTP 端口必须对整个世界开放。您可以通过提供覆盖文件或 FirewallD 来使用 systemd 方法。

  4. 部署一个新的 VM,选择您喜欢的发行版和版本。

  5. 使用变量创建新的/etc/hosts文件。如果您使用 PowerShell DSC,您还需要 PowerShell 来完成此任务。对于专家:使用资源组中其他计算机的主机名和 IP 地址。

进一步阅读

我们真的希望你喜欢这个对编排平台的介绍。这只是一个简短的介绍,让你对更多的学习产生好奇。本章提到的编排工具的所有网站都是很好的资源,阅读起来很愉快。

还有一些额外的资源需要提到,包括以下内容:

  • 詹姆斯·波格兰的《学习 PowerShell DSC-第二版》。

  • Ansible:我们确实认为 Russ McKendrick 的《学习 Ansible》以及同一作者关于 Ansible 的其他书籍都值得赞扬。如果你懒得读这本书,那么你可以参考 Ansible 的文档开始学习。如果你想要一些实践教程,你可以使用这个 GitHub 仓库:github.com/leucos/ansible-tuto

  • Terraform:在 Microsoft Azure 上使用 Terraform-第一部分:介绍是由微软高级软件工程师 Julien Corioland 撰写的博客系列。该博客包括一系列讨论 Azure 上 Terraform 的主题。值得一读并尝试这些任务。博客地址为blog.jcorioland.io/archives/2019/09/04/terraform-microsoft-azure-introduction.html

  • Mayank Joshi 的《精通 Chef》

  • Jussi Heinonen 的《学习 Puppet》

第九章:Azure 中的容器虚拟化

第二章“开始使用 Azure 云”中,我们开始了在 Azure 中的旅程,首先在 Azure 中部署了我们的第一个工作负载:Linux 虚拟机的部署。之后,我们涵盖了 Linux 操作系统的许多方面。

第七章“部署您的虚拟机”中,我们探讨了部署虚拟机的几种选项,而第八章“探索持续配置自动化”则是关于使用编排工具进行配置管理之后要做的事情。

编排是 DevOps 运动的一个不断增长的部分。DevOps 是打破组织中经典隔离的一部分。参与开发、测试和部署产品的不同团队必须进行沟通和合作。DevOps 是文化哲学、实践和工具的结合。DevOps 是一种使部署增量、频繁和常规事件的方式,同时限制失败影响的方法。

虚拟机不是部署工作负载的唯一方式:您还可以在容器中部署工作负载。它使得与编排一起,可以满足 DevOps 的要求。

因此,在我们实际学习和在 Azure 中实现容器之前,让我们快速看一下本章提供了什么。在本章结束时,您将:

  • 了解容器的历史,并了解容器化的早期采用情况。

  • 熟悉诸如systemd-nspawn和 Docker 之类的容器工具。

  • 能够使用 Docker Machine 和 Docker Compose。

  • 能够使用 Azure 容器实例和 Azure 容器注册表。

  • 了解新一代容器工具,如 Buildah、Podman 和 Skopeo。

现在,我们首先要了解容器是什么,以及它是如何发展的。

容器技术简介

第一章“探索 Azure 云”中,我们简要介绍了容器。因此,让我们继续更详细地介绍容器。我们知道虚拟机是在 hypervisor 上运行的,并且在大多数情况下,您必须为每个目的创建一个单独的虚拟机来隔离环境。虚拟机将有一个类似 Linux 的客户操作系统,然后我们将安装所需的软件。在某些情况下,您必须部署大量的虚拟机进行测试。如果您正在使用运行 Hyper-V 的本地基础设施,您必须考虑资源利用率,即每个虚拟机将使用多少内存、CPU 等。如果您在 Azure 中部署,您还必须考虑成本。您可能只需要一些虚拟机来测试某些东西,但这些虚拟机的占用空间很大;它们实际上是在虚拟运行的完整计算机。另一个问题是兼容性问题。假设您有一个应用程序需要一个依赖包,比如 Python 2.2。现在想象一下在同一个虚拟机中运行另一个应用程序,它与 Python 2.2 存在兼容性问题,只能使用 Python 2.1。您最终将不得不为第二个应用程序创建一个新的虚拟机,其中安装了 Python 2.1。为了克服这个问题,引入了容器。以下是容器与虚拟机有何不同的图示表示:

VMs 与容器的图示表示不同

图 9.1:虚拟机和容器的表示

与虚拟机一样,容器允许您将应用程序与所有依赖项和库打包在一起。它们像虚拟机一样是隔离的环境,可以用于测试和运行应用程序,而无需创建多个虚拟机。容器也很轻量级。

与虚拟机不同,容器是在操作系统级别进行虚拟化的,而不是每个硬件组件都进行虚拟化。这意味着容器的占用空间比虚拟机小。例如,Ubuntu ISO 镜像的大小接近 2.4 GB;另一方面,Ubuntu 容器镜像小于 200 MB。考虑之前的例子,我们在 Python 2.2 上有依赖问题,最终创建了两个虚拟机。使用容器,我们可以有两个占用空间比两个虚拟机小得多的容器。此外,主机操作系统的成本和资源利用远远低于两个虚拟机。容器使用容器运行时部署;有不同的运行时可用。在本章中,我们将看一下流行的容器运行时。

容器不是圣杯。它不能解决您所有的问题。但是,您可以考虑以下情景,如果它们中的任何一个符合您的要求,您可能希望将应用程序容器化:

  • 应用程序经常需要更新新功能,最好无需停机,这是由业务需求驱动的。

  • 系统工程师和开发人员可以共同解决业务需求,并对彼此的领域有足够的理解和知识(而不必成为两者的专家),并且具有持续实验和学习的文化。

  • 为了使应用程序更好,需要有失败的空间。

  • 应用程序不是单点故障。

  • 应用程序在可用性和安全性方面不是关键应用程序。

还有一件小事:如果您有许多不同类型的应用程序,并且这些应用程序之间几乎没有共享的代码,容器技术仍然是一个选择,但在这种情况下,虚拟机可能是更好的解决方案。

我们将简要介绍容器技术的历史,以便让您更好地了解其来源。我们将探讨当今提供的一些解决方案:systemd-nspawn 和 Docker。甚至有更多的容器虚拟化实现可用,甚至一些最早的实现,比如 LXC。实际上,无论您使用哪种容器化工具:如果您了解容器背后的思想和概念,就很容易使用其他工具实现相同的思想和概念。唯一改变的是命令;所有这些工具的基本概念都是相同的。

容器的历史

容器现在非常流行。但它们并不新;它们不是突然出现的。很难指出它们开始的确切时间。我们不想给您历史课,但历史可以让您了解技术,甚至可以让您了解为什么或何时应该在组织中使用容器。

因此,我们不会专注于确切的时间轴,我们只会涵盖重要的步骤:实施如果您想了解当今容器技术的重要技术。

chroot 环境

在 Linux 中,有一个根文件系统,如第五章 高级 Linux 管理中所述,一切都挂载到该文件系统上,这将对当前运行的进程及其子进程可见。

chroot中运行的进程有自己的根文件系统,与系统范围的根完全分离,称为fs.chroot。它经常用于开发,因为在chroot中运行的程序无法访问其根文件系统之外的文件或命令。要从目录启动 chroot 监狱,请执行以下操作:

chroot /<directory>

1979 年,在 Unix 的第 7 版中引入了chroot系统调用,并在 1982 年引入了 BSD Unix。Linux 自其存在的早期就实现了这个系统调用。

OpenVZ

2005 年,几乎与 Solaris 启动其容器技术同时,一家名为 Virtuozzo 的公司启动了 OpenVZ 项目。

他们采用了 chroot 环境的原则,并将其应用于其他资源。chroot 进程将具有以下内容:

  • 根文件系统

  • 用户和组

  • 设备

  • 一个进程树

  • 一个网络

  • 进程间通信对象

当时,OpenVZ 被视为基于 hypervisor 的虚拟化的轻量级替代方案,也被视为开发人员的坚实平台。它仍然存在,并且您可以在任何 Linux 操作系统上使用它,无论是在云中还是不在云中。

使用 OpenVZ 类似于使用虚拟机:您可以创建一个带有您喜欢的发行版基本安装的映像,之后您可以使用编排来安装应用程序并维护一切。

LXC

2006 年,Google 的工程师开始在 Linux 内核中开发一个名为cgroups控制组)的功能,以便对进程集合(资源组)上的 CPU、内存、磁盘 I/O 和网络等资源进行资源控制。

Linux 内核的一个相关特性是cgroups的概念成为了一个命名空间。

2008 年,cgroups被合并到 Linux 内核中,并引入了一个新的命名空间,即user命名空间。然后,这两种技术为容器的新步骤——LXC 启用了。

其他可用的命名空间包括pidmountnetworkuts(自己的域名)和ipc

不再需要跟踪 Linux 内核的开发情况:每个所需的组件都可用,并且资源管理更加出色。

最近,Canonical 开发了一个名为 LXD 的新容器管理器,它在其后端使用 LXC,并旨在提供改进的用户体验来管理容器。从技术上讲,LXD 使用 LXC 通过 liblxc 和其 Go 绑定来实现这一目标。以下列出了 LXD 的一些优点:

  • 安全

  • 高度可扩展

  • 简化资源共享

systemd-nspawn

systemd 带有一个容器解决方案。它起初是一个实验,然后 Lennart Poettering 认为它已经准备好投入生产。实际上,它是另一个解决方案 Rkt 的基础。在撰写本书时,Rkt 的开发已经停止。但是,您仍然可以访问 Rkt GitHub 存储库(github.com/rkt/rkt)。

systemd-nspawn 并不是很出名,但它是一种强大的解决方案,可在每个现代 Linux 系统上使用。它建立在内核命名空间和 systemd 之上进行管理。这是一种类似于增强版的 chroot。

如果您想了解更多关于容器底层技术的知识,systemd-nspawn 是一个很好的起点。在这里,每个组件都是可见的,如果您愿意,可以手动配置。systemd-nspawn 的缺点是您必须自己完成所有工作,从创建映像到编排再到高可用性:这一切都是可能的,但您必须自己构建。

容器也可以使用诸如yum之类的软件包管理器创建,并通过提取原始云映像(几个发行版提供此类映像,例如cloud.centos.org/centos/7/imagescloud-images.ubuntu.com/)。您甚至可以使用 Docker 映像!

如前所述,有多种方法可以创建容器。例如,我们将介绍其中的两种:debootstrapyum

使用 debootstrap 创建容器

debootstrap实用程序是一个工具,可以将基于 Debian 或 Ubuntu 的系统安装到已安装系统的子目录中。它在 SUSE、Debian 和 Ubuntu 的存储库中可用;在 CentOS 或其他基于 Red Hat 的发行版上,您需要从企业 Linux 的额外软件包EPEL)存储库中获取它。

例如,让我们在 CentOS 机器上引导 Debian,以创建我们的 systemd 容器的模板。

在本章中,如果您正在运行 CentOS,您必须更改 systemd-nspawn 的安全标签:

semanage fcontext -a -t virtd_lxc_exec_t /usr/bin/systemd-nspawn
 restorecon -v /usr/bin/systemd-nspawn

首先,安装 debootstrap:

sudo yum install epel-release
sudo yum install debootstrap

创建一个子目录:

sudo mkdir -p /var/lib/machines/releases/stretch
sudo -s
cd /var/lib/machines/releases

例如,从 Debian 的美国镜像引导:

debootstrap --arch amd64 stretch stretch \
  http://ftp.us.debian.org/debian

使用 yum 创建容器

yum实用程序在每个存储库中都可用,并且可用于创建具有基于 Red Hat 的发行版的容器。

让我们来看看创建 CentOS 7 容器的步骤:

  1. 创建一个目录,我们将在其中安装 CentOS,并将其用作我们的模板:
sudo mkdir -p /var/lib/machines/releases/centos7
sudo -s
cd /var/lib/machines/releases/centos7

首先,您必须在mirror.centos.org/centos-7/7/os/x86_64/Packages/下载centos-release rpm软件包。

  1. 初始化rpm数据库并安装此软件包:
rpm --rebuilddb --root=/var/lib/machines/releases/centos7
rpm --root=/var/lib/machines/releases/centos7 \
  -ivh --nodeps centos-release*rpm
  1. 现在您已经准备好至少安装最低限度的内容了:
yum --installroot=/var/lib/machines/releases/centos7 \
  groups install  'Minimal Install'

安装软件包后,将可用完整的根文件系统,提供启动容器所需的一切。您还可以使用此根文件系统作为模板;在这种情况下,您需要修改模板以确保每个容器都是唯一的。

systemd-firstboot

systemd-firstboot 是在首次启动容器时配置一些内容的好方法。您可以配置以下参数:

  • 系统区域设置(--locale=

  • 系统键盘映射(--keymap=

  • 系统时区(--timezone=

  • 系统主机名(--hostname=

  • 系统的机器 ID(--machine-id=

  • Root 用户的密码(--root-password=

您还可以使用-prompt参数在首次启动时请求这些参数。

在以下示例中,我们将修改systemd-firstboot单元,以传递在首次运行容器时将执行的配置。

在容器目录中执行chroot。让我们以我们的 CentOS 镜像为例:

chroot /var/lib/containers/releases/centos7
passwd root

启动镜像:

systemd-nspawn --boot -D centos7

打开systemd-firstboot单元,/usr/lib/systemd/system/systemd-firstboot.service,并对其进行修改:

[Unit]
Description=First Boot Wizard
Documentation=man:systemd-firstboot(1)
DefaultDependencies=no
Conflicts=shutdown.target
After=systemd-readahead-collect.service systemd-readahead-replay.service systemd-remount-fs.service
Before=systemd-sysusers.service sysinit.target shutdown.target
ConditionPathIsReadWrite=/etc
ConditionFirstBoot=yes
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/systemd-firstboot --locale=en_US-utf8 --root-password=welk0mITG! --timezone=Europe/Amsterdam 
StandardOutput=tty
StandardInput=tty
StandardError=tty

启用服务:

systemctl enable systemd-firstboot

清理设置:

 rm /etc/\
  {machine-id,localtime,hostname,shadow,locale.conf,securetty}

使用Ctrl + D退出 chroot 环境。

部署第一个容器

如果您正在使用 BTRFS 文件系统模板目录作为子卷,可以使用systemd-nspawn--template参数。否则,它将创建一个新的子卷:

cd /var/lib/machines/releases
cp -rf centos7/ /var/lib/machines/centos01

是时候启动我们的第一个容器了:

systemd-nspawn --boot -D centos01

尝试登录并使用Ctrl + ]]]杀死它。

从现在开始,您可以使用machinectl命令管理容器:

machinectl start <machine name>

使用以下登录:

machinectl login <machine name>

machinectl还有许多其他参数值得研究!如果收到权限被拒绝的消息,请考虑 SELinux 故障排除!此外,journalctl有一个-M参数,用于查看容器内的日志,或者使用以下命令:

journalctl _PID=<pid of container> -a

如果您在容器中执行hostnamectl,您将看到类似以下内容:

hostnamectl 命令的详细输出

图 9.2:hostnamectl 命令的输出

内核是主机的内核!

在启动时启用容器

要使容器在启动时可用,请启用目标machines.target

sudo systemctl enable machines.target

现在为我们的容器创建一个nspawn文件:/etc/systemd/nspawn/centos01.nspawn。文件名必须与容器相同:

[Exec]
PrivateUsers=pick
[Network]
Zone=web
Port=tcp:80
[Files]
PrivateUsersChown=yes

[Network]还设置了从容器的 TCP 端口80到主机的端口80的端口转发。您必须在容器中的网络接口上配置 IP 地址,并在子网中的虚拟以太网接口上配置主机上的 IP 地址,以使其正常工作。

现在启用 VM:

sudo machinectl enable centos01

现在您知道如何使用systemd-nspawn并部署您的容器,让我们继续讨论最流行的容器化工具:Docker。您可能已经听说过很多关于 Docker 的事情,所以让我们开始吧!

Docker

2010 年 3 月,Solomon Hykes 开始开发 Docker。它在法国作为内部 dotCloud 开始。由于 2013 年在一次大型 Python 会议上的公开发布以及 Red Hat 的兴趣,Docker 真正起飞。在同年的最后一个季度,公司的名称更改为 Docker Inc。

Docker 最初是建立在 LXC 之上的,但过了一段时间,LXC 被他们自己的libcontainer库所取代。

Docker 的架构非常复杂:它由客户端、Docker 和守护进程dockerd组成。另一个守护进程containerd是操作系统和正在使用的容器技术类型的抽象层。您可以使用docker-containerd-ctr工具与containerd进行交互。containerd守护进程负责以下内容:

  • 注册表(您可以存储镜像的地方)

  • 镜像(构建、元数据等)

  • 网络

  • 卷(用于存储持久数据)

  • 签名(内容的信任)

containerd与 RunC 通信,RunC 负责以下内容:

  • 生命周期管理

  • 运行时信息

  • 在容器内运行命令

  • 生成规格(镜像 ID、标签等)

Docker 有两个版本可用——Docker 社区版CE)和Docker 企业版EE)。Docker EE 于 2019 年 11 月由 Docker Inc 出售给 Mirantis;然而,Docker CE 仍由 Docker Inc 处理。Docker EE 增加了 Docker 支持,还有集成的安全框架,认证插件,对 Docker Swarm(类似于 Kubernetes 的容器编排解决方案)的支持,以及对 RBAC/AD/LDAP 的支持。然而,所有这些都是需要付费的。如果您觉得您的环境需要这些额外的优势,那么值得付费。另一方面,Docker CE 是开源软件,可以免费使用。

Docker 安装

在 Azure 中有多种安装和使用 Docker CE 的方法。您可以安装您选择的 Linux 发行版,并在其上安装 Docker。Azure Marketplace 中提供了几个 VM,如 RancherOS,这是一个非常精简的 Linux 发行版,专门用于运行 Docker。最后但同样重要的是,还有 Docker for Azure 模板,由 Docker 提供,网址为docs.docker.com/docker-for-azuredocs.docker.com/docker-for-azure

对于本章的目的,Ubuntu 服务器 VM 上的 Docker 绝对不是一个坏主意;它可以节省很多工作!但是有几个原因不使用这个 VM:

  • 如果您自己配置一切,确实可以帮助更好地理解事物。

  • 使用的软件相对较旧。

  • 用于创建 VM 的 Docker VM 扩展已被弃用,不再处于活跃开发状态。

Docker for Azure 模板还安装并配置了 Docker Swarm,这是一个 Docker 本地的集群系统。

Docker 网站提供了关于如何手动安装 Docker 的优秀文档。如果您想使用aptyum进行安装而不是按照脚本,可以按照官方 Docker 文档进行操作(docs.docker.com/v17.09/engine/installation/#supported-platforms)。如果您按照这个方法操作,可以跳过cloud-init脚本。

在这里,我们将按照我们的脚本进行安装。请注意,这个脚本对实验环境很方便,但不适用于生产环境。

它从 Edge 渠道安装最新版本的 Docker,而不是从 Stable 渠道。理论上,这可能会有点不稳定。

然而,对于本章的目的,这是一个很好的开始方式。为了快速启动和运行,让我们使用第七章,部署您的虚拟机中学到的 cloud-init 技术。

首先创建一个新的资源组,例如Docker_LOA

az group create --name Docker_LOA --location westus

创建一个 cloud-init 配置文件;在我的示例中,文件名为docker.yml,内容如下:

#cloud-config
package_upgrade: true
write_files:
- content: |
    [Service]
    ExecStart=
    ExecStart=/usr/bin/dockerd
  path: /etc/systemd/system/docker.service.d/docker.conf
- content: |
    {
      "hosts": ["fd://","tcp://127.0.0.1:2375"]
    }
  path: /etc/docker/daemon.json
runcmd:
 - curl -sSL https://get.docker.com/ | sh
 - usermod -aG docker <ssh user>

不要忘记用您用于执行az命令的帐户的登录名替换<ssh user>

你可能已经注意到我们在脚本中添加了 ExecStart 两次。ExecStart 允许你指定在启动单元时需要运行的命令。通过设置 ExecStart= 并在第二行指定实际命令,可以清除它是一个好习惯。原因是当 Docker 安装时,它将最初具有 ExecStart 值,当我们提供另一个值时,将导致冲突。这种冲突将阻止服务启动。让我们继续使用我们创建的 cloud-init 文件创建一个安装了 Docker 的虚拟机:

  1. 创建一个安装了你选择的发行版的虚拟机:
az vm create --name UbuntuDocker --resource-group Docker_LOA \
  --image UbuntuLTS --generate-ssh-keys --admin-username <ssh-user> \ 
  --custom-data docker.yml
  1. 当虚拟机准备就绪后,登录并执行以下操作:
sudo systemctl status docker.service

注意

如果你收到一条消息说 "Warning: docker.service changed on disk, run systemctl daemon-reload to reload docker.service",请耐心等待,cloud-init 仍在忙碌。另外,如果你看到 docker.service 未找到,请等待一段时间让 cloud-init 完成安装。你可以通过执行 dpkg -l | grep docker 来验证 Docker CE 是否已安装。

  1. 执行以下命令以获取有关 Docker 守护程序的更多信息:
docker info
  1. 是时候下载我们的第一个容器并运行它了:
docker run hello-world

在下面的截图中,你可以看到容器成功运行,并收到了 Hello from Docker! 消息:

使用 docker run hello-world 命令成功执行容器

图 9.3:成功执行容器

Docker 容器是一个执行的镜像。要列出系统中可用的镜像,请执行以下操作:

docker image ls

在上一个例子中,我们运行了 docker run hello-world。因此,镜像已经被拉取,并且当我们使用 docker image ls 命令时,你可以看到 hello-world 镜像被列出来了:

使用 docker image 列出 hello-world 镜像

ls 命令](https://gitee.com/OpenDocCN/freelearn-linux-zh/raw/master/docs/hsn-linux-adm-az/img/B15455_09_04.jpg)

图 9.4:列出 Docker 镜像

如果你再次执行 docker run hello-world,这次镜像将不会被下载。相反,它将使用在上一次运行期间已经存储或下载的镜像。

让我们下载另一个镜像:

docker run ubuntu

之后,我们将列出所有容器,即使那些未运行的:

docker ps -a

所有容器都具有 exited 状态。如果你想保持容器运行,你必须在运行命令中添加 -dt 参数;-d 表示分离运行:

docker run -dt ubuntu bash

如果你想要一个交互式 shell 到 Ubuntu 容器(就像你 SSH 到一个虚拟机一样),你可以添加 -i 参数:

   docker run -it ubuntu

通过再次查看进程列表来验证它是否正在运行:

docker ps

使用容器的 ID 或名称,你可以在容器中执行命令,并在终端中接收标准输出:

docker exec <id/name> <command>

例如,你可以执行以下命令来查看容器镜像的操作系统版本:

docker exec <id/name> cat /etc/os-release

附加到容器以验证内容是否符合预期:

docker attach <id/name>

并使用 Ctrl + PCtrl + Q 分离,这意味着你将退出交互式 shell,容器将在后台运行。

总之,如果你一直在跟着做,到目前为止,你将能够运行容器,将它们作为分离运行,从主机机器执行容器命令,以及获取容器的交互式 shell。到目前为止,我们已经使用了 Docker Hub 中已经可用的镜像。在下一节中,我们将学习如何从基础镜像构建自己的 Docker 镜像。

构建 Docker 镜像

Docker 镜像包含层。对于你运行的每个命令来向容器添加组件,都会添加一个层。每个容器都是一个具有只读层和可写层的镜像。第一层是引导文件系统,第二层称为基础层;它包含操作系统。你可以从 Docker Registry 拉取镜像(稍后你将了解更多关于 Registry 的信息),或者自己构建镜像。

如果您想自己构建一个,可以以类似的方式进行,就像我们之前看到的那样,使用 systemd-nspawn 容器,例如,通过使用 debootstrap。大多数命令需要 root 用户访问权限,因此请按以下方式提升您的权限:

sudo -i

让我们以 Debian 作为基本镜像。这将帮助您了解docker import命令。下载并提取 Debian Stretch:

debootstrap --arch amd64 stretch stretch \
  http://ftp.us.debian.org/debian

创建一个 tarball 并将其直接导入 Docker:

tar -C stretch -c . | docker import - stretch

使用以下命令进行验证:

docker images

Docker 还提供了一个名为scratch的非常小的基本镜像。

Docker 镜像是从 Dockerfile 构建的。让我们创建一个工作目录来保存 Dockerfile:

mkdir ~/my-image && cd ~/my-image

由于stretch镜像已经在 Docker Hub 中可用,因此最好使用新名称标记您的镜像,以便 Docker 不会尝试拉取镜像,而是选择本地镜像。要标记镜像,请使用以下命令:

docker tag stretch:latest apache_custom:v1 

然后,通过执行vi Dockerfile(您可以使用任何文本编辑器)创建一个 Dockerfile。此文件中的第一行将基本镜像添加为一个层:

FROM apache_custom:v1 

第二层包含 Debian 更新:

RUN apt-get --yes update

第三层包含 Apache 安装:

RUN apt-get --yes install apache2

添加最新的层并在这个读/写层中运行 Apache。CMD用于指定执行容器的默认值:

CMD /usr/sbin/apachectl -e info -DFOREGROUND

打开端口80

EXPOSE 80

保存文件,您的文件条目将如下截图所示。添加注释是一个好习惯;但是,这是可选的:

使用 cat Dockerfile 创建 Docker 镜像

图 9.5:创建 Docker 镜像

构建容器:

docker build -t apache_image .

如果一切顺利,输出应该显示类似于以下内容:

表示成功构建 Docker 镜像的输出

图 9.6:成功构建 Docker 镜像

您可以测试容器:

docker run -d apache_image 

查看构建历史:

docker history <ID/name>

如下截图所示,您将能够查看容器的构建历史:

显示构建容器的详细输出

图 9.7:审查构建的容器历史

执行docker ps以获取容器的 ID,并使用该 ID 收集有关容器的信息:

docker inspect <ID/name> | grep IPAddress

在输出中,您可以找到容器的 IP 地址:

容器的 IP 地址输出

图 9.8:获取 Docker 的 IP 地址

使用curl查看 Web 服务器是否真的在运行:

curl <ip address>

您将能够在此处看到著名的 HTML“它起作用”页面:

使用 curl 命令测试 Web 服务器

图 9.9:使用 curl 命令测试 Web 服务器

现在,我们将使用以下命令停止容器:

docker stop <ID>

现在再次运行它:

docker run -d <ID> -p 8080:80

这将使网站在本地主机端口8080上可用。

您还可以使用acbuild来构建 Docker 容器。

Docker Machine

还有另一种创建 Docker 容器的方法:Docker Machine。这是一个创建将托管 Docker 的 VM 的工具。这是您应该在开发机器上运行的东西,无论是物理的还是虚拟的,您都应该远程执行所有操作。

请注意,Docker Machine 可以安装在 macOS、Linux 和 Windows 机器上。请参考 Docker Machine 文档(docs.docker.com/machine/install-machine/)以获取 macOS 和 Windows 安装信息,因为我们只关注 Linux 安装。

切换回安装了 Docker 的 Ubuntu 机器。安装以下依赖项:

sudo apt install sshfs

接下来,您需要下载 Docker Machine,然后将其提取到您的PATH中:

base=https://github.com/docker/machine/releases/download/v0.16.0 \
&& curl -L $base/docker-machine-$(uname -s)-$(uname -m) \
>/tmp/docker-machine && \
sudo mv /tmp/docker-machine /usr/local/bin/docker-machine \
&& chmod +x /usr/local/bin/docker-machine

自动补全可能非常有用,还要确保以 root 身份运行以下脚本,因为脚本将写入/etc/目录:

base=https://raw.githubusercontent.com/docker/machine/v0.16.0
for i in docker-machine-prompt.bash docker-machine-wrapper.bash \
docker-machine.bash
do
  sudo wget "$base/contrib/completion/bash/${i}" -P \ /etc/bash_completion.d
source /etc/bash_completion.d/$i
done

注销并重新登录。为了验证bash-completion是否有效,您可以点击 tab 按钮查看docker-machine的可用命令,如下截图所示:

使用 docker-machine 命令验证 bash-completion

图 9.10:验证 bash-completion 是否成功

验证版本:

docker-machine version

使用 Azure 作为驱动程序,现在可以部署 VM:

docker-machine create -d azure \
  --azure-subscription-id <subscription id> \
  --azure-ssh-user <username> \
  --azure-open-port 80 \
  --azure-size <size> <vm name>

还有其他选项,例如公共 IP 和资源组名称,可以在部署期间传递。 您可以在 Docker 文档中查看这些选项的完整列表和默认值(docs.docker.com/machine/drivers/azure/)。 如果我们没有为特定选项指定值,Docker 将采用默认值。 还要记住的一件事是,VM 名称应仅包含小写字母数字字符或必要时连字符; 否则,您将收到错误。

在下面的屏幕截图中,您可以看到名称为docker-machine-2的 VM 的部署成功,并且 Docker 正在该机器上运行。 为了简单起见,我们已将订阅 ID 保存到变量$SUB_ID中,这样我们就不必每次都检查它; 如果需要,您也可以这样做。 由于我们之前已经进行了身份验证,因此驱动程序不会要求我们再次登录。 驱动程序会记住您的凭据长达两周,这意味着您不必每次部署时都登录。 您还可以查看部署了哪些资源:

代表 docker-machine-2 VM 成功部署的输出

图 9.11:部署 docker-machine-2 VM

要告诉 Docker 使用远程环境而不是在本地运行容器,请执行以下操作:

docker-machine env <vm name>
eval $(docker-machine env <vm name>)

要验证是否正在使用远程环境,请使用info命令:

docker info

在其他信息中,输出显示您正在使用在 Azure 中运行的特定 VM:

使用 dockor info 命令详细了解 Docker 的信息

图 9.12:获取 docker 信息

对于 Docker Machine,执行以下命令:

docker-machine ls

输出应类似于以下内容:

列出 Docker 机器的各种详细信息

图 9.13:列出 docker-machine

让我们创建一个 nginx 容器,将主机端口80映射到容器端口80。 这意味着所有发送到主机 VM 端口80的流量将被定向到容器的端口80。 这是使用-p参数给出的。 执行以下命令创建 nginx 容器:

docker run -d -p 80:80 --restart=always nginx

查找 VM 的 IP 地址:

docker-machine ip <vm name>

在浏览器中使用该 IP 地址验证 nginx 是否正在运行。

Docker Machine 还允许我们使用scp参数将文件复制到 VM 中,甚至可以在本地挂载文件:

mkdir -m 777 /mnt/test
docker-machine mount <vm name>:/home/<username> /mnt/test

使用docker ps查找正在运行的实例,停止它们并删除它们,以便它们为下一个实用程序做好准备。

Docker Compose

Docker Compose 是用于创建多容器应用程序的工具,例如,需要 Web 服务器和数据库的 Web 应用程序。

您可以在github.com/docker/compose/releases上检查 Docker Compose 的最新或稳定版本,并安装它,将命令中的版本号替换为最新版本:

sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

现在,将可执行权限应用于我们下载的二进制文件:

sudo chmod +x /usr/local/bin/docker-compose

接下来,验证安装:

docker-compose version

如果安装成功,您将能够看到安装的 Docker Compose 的版本:

验证 Dockor Compose 信息的版本

图 9.14:验证 Docker compose 安装

注意

安装后,如果前面的命令失败,请检查您的路径,否则在/usr/bin或您的路径中的任何其他目录中创建符号链接。 要找出您的PATH中有哪些目录,请在 shell 中执行$PATH。 要创建符号链接,请执行sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

创建名为docker-compose.yml的文件,并包含以下内容:

wordpress:
  image: wordpress
  links:
    - db:mysql
  ports:
    - 80:80
db:
  image: mariadb
  environment:
    MYSQL_ROOT_PASSWORD: <password>

<password>替换为您选择的密码。 在仍然连接到 Azure 环境的情况下,使用 Docker Machine 执行以下操作:

docker-compose up -d

如果构建成功,将运行两个容器,您可以使用docker ps进行验证,并使用正确的 IP 地址(docker-machine ip <vm name>)在浏览器中打开 WordPress 安装程序。

Docker 注册表

每次执行docker rundocker pull(仅下载)时,都会从互联网获取镜像。它们来自哪里?运行此命令:

docker info | grep Registry

上述命令的输出给出了答案:index.docker.io/v1/。此 URL 是官方的 Docker Hub。Docker Hub,或 Docker Store,还有一个可通过hub.docker.com访问的漂亮的 Web 界面,是私有和公开可用的 Docker 镜像的在线存储库。

docker search命令可用于搜索此存储库。要限制此命令的输出,可以添加过滤器:

docker search --filter "is-official=true" nginx --no-trunc

以下是docker search命令的输出:

docker search 命令的输出

图 9.15:docker search 命令的输出

可选地,添加--no-trunc参数以查看镜像的完整描述。输出中还有一个星级评分,可以帮助我们选择最佳可用镜像。

如果您在 Docker Hub 网站上创建自己的帐户,可以使用docker push将您的镜像上传到注册表。这是免费的!

使用以下信息登录:

docker login -u <username> -p <password>

构建镜像:

docker build -t <accountname>/<image>:versiontag .

您还可以在之后为镜像打标签:

docker tag <tag id> <accountname>/<image>:versiontag

对于版本控制,建议使用诸如v1.11.1.2019之类的字符串,表示第一个版本于 2019 年 11 月 1 日发布。如果不添加版本,它将被标记为最新版本。

您无法使用docker search命令查看标签。您需要使用 Web 界面或使用curl(用于在服务器和客户端之间传输数据的工具)和jq(一种类似于sed但专门用于 JSON 数据的工具)来查询 Docker API:

wget -q https://registry.hub.docker.com/v1/repositories/<image>/tags -O - | jq 

注意

默认情况下未安装 jq。您必须使用apt install jq进行安装。

此输出将以 JSON 格式呈现。您可以使用jq进一步查询并根据需要优化输出。如果您不想使用 jq 格式化 JSON,可以使用本机的sedtrcut命令来格式化输出并获得更清晰的内容:

wget -q https://registry.hub.docker.com/v1/repositories/<image name>/tags -O -  | sed -e 's/[][]//g' -e 's/"//g' -e 's/ //g' | tr '}' '\n' | cut -d ":" -f3

如果您想获取 nginx 的所有标签,可以将<image name>替换为nginx

我们已经讨论了 Docker Hub 以及如何检查可用镜像。类似地,Azure 提供了 Azure 容器注册表,您可以在其中存储私有镜像,并在需要时拉取它们。在开始使用 Azure 容器注册表之前,我们需要了解 Azure 容器实例,借助它,您可以在不必管理主机机器的情况下运行容器。让我们继续学习更多。

Azure 容器实例

现在我们能够在虚拟机中运行容器,我们可以更进一步:我们可以使用 Azure 容器实例服务来运行它,而无需管理服务器。

您可以在 Azure 门户中执行此操作。在左侧导航栏中,选择所有服务并搜索容器实例。一旦进入容器实例,点击添加以创建新的容器实例,门户将重定向到以下窗口:

在 Azure 容器实例门户上创建新的容器实例

图 9.16:创建 Docker 容器实例

您可以创建一个资源组或使用现有的资源组。将容器名称设置为nginx,设置Public,因为我们将要拉取一个公共镜像,将镜像名称设置为nginx:latest,设置Linux,并选择容器的所需资源要求。点击下一步,在网络部分,我们将公开端口 80以进行 HTTP 流量,如下截图所示。此外,如果需要,您可以添加DNS 标签并选择公共 IP 地址:

为容器实例添加网络详细信息

图 9.17:为容器实例添加网络详细信息

这已足够验证和创建实例。您可以跳过下一节,转到审阅+创建。但是,Azure 在高级选项卡中提供了高级选项。这些选项可用于添加环境变量,设置重启策略选项,并使用命令覆盖以包括在容器初始化时需要执行的一组命令。如果您愿意,也可以配置这些选项。

您还可以使用 Azure CLI 命令行创建容器:

az container create --resource-group <resource group> --name nginx --image nginx:latest --dns-name-label nginx-loa --ports 80

您也可以使用 PowerShell:

New-AzContainerGroup -ResourceGroupName <resource group> '
  -Name nginx -Image nginx:latest r -OsType Linux '
  -DnsNameLabel nginx-loa2

请注意,DNS 标签在您的区域必须是唯一的。

在命令的输出中,实例的 IP 地址是可见的:

使用 PowerShell 创建容器

图 9.18:使用 PowerShell 创建容器

您应该能够访问 FQDN 和 IP 地址上的 Web 服务器。如屏幕截图所示,您可以将浏览器指向 DNS 标签或 IP 地址,然后您可以看到欢迎使用 nginx!页面:

当浏览器指向 DNS 标签时,Web 页面的输出

图 9.19:当浏览器指向 DNS 标签时,Web 服务器的输出

要获取容器实例的列表,请执行以下操作:

az container list

或者,执行以下操作:

Get-AzContainerGroup | Format-List

到目前为止,我们一直依赖 Docker Registry 来保存、拉取和推送图像。Azure 提供了一个私有图像注册表,您可以在其中存储图像,以便在需要时使用。这项服务称为 Azure 容器注册表。让我们了解一下。

Azure 容器注册表

如前所述,您可以使用私有 Azure 容器注册表,而不是 Docker 注册表。这项服务是收费的!使用此 Azure 服务的优势在于,您拥有 Blob 存储的所有功能(可靠性、可用性、复制等),并且可以将所有流量保持在 Azure 内部,这使得该注册表在功能、性能和成本方面成为一个有趣的选择。

使用 Azure 门户

创建注册表的最简单方法是使用 Azure 门户。在左侧导航栏中,选择所有服务,然后搜索容器注册表。单击添加,您应该会看到以下屏幕。不要忘记启用管理员用户选项;这样做可以让您使用用户名作为注册表名称和访问密钥作为密码登录到容器注册表:

使用 Azure 门户创建容器注册表

图 9.20:使用 Azure 门户创建容器注册表

如果注册表准备就绪,将会弹出一个弹窗,显示作业已完成,并且您将能够看到资源。如果导航到访问密钥刀片窗格,您将找到登录服务器和您的用户名,该用户名与注册表名称相同,并且一组密码:

访问密钥刀片窗格的屏幕截图

图 9.21:访问密钥刀片窗格

使用此信息登录存储库,方式与您在 Docker Hub 上登录相同。

推送图像后,它将在存储库中可用。从那里,您可以将其部署到 Azure 容器实例服务并运行它。

使用 Azure CLI

我们已经通过 Azure 门户创建了一个 Azure 容器注册表实例。也可以使用 Azure CLI 和 PowerShell 执行相同的任务。我们将遵循 Azure CLI 步骤,并鼓励您尝试使用 PowerShell 自行完成此过程。

首先,我们需要一个已安装 Docker 和 Azure CLI 的 Linux VM。

让我们首先创建一个资源组,或者您可以使用在门户示例中使用的相同资源组。只是为了回忆我们在* Docker 安装*部分学习的命令,我们将继续使用一个新的资源组:

az group create --name az-acr-cli --location eastus

一旦收到成功消息,请继续使用以下命令创建容器注册表:

az acr create --resource-group az-acr-cli --name azacrcliregistry --sku Basic --admin-enabled true

在这里,我们使用基本 SKU 创建容器注册表。还有其他可用的 SKU,提供更多的存储选项和吞吐量。SKU 指向容器注册表的不同定价层。访问 Microsoft Azure 定价页面(azure.microsoft.com/en-in/pricing/details/container-registry/)查看每个 SKU 的定价。由于这是一个演示,并且为了保持成本最低,我们将选择基本 SKU。

部署 Azure 容器注册表实例后,我们将登录到注册表。但是要登录,我们需要密码。我们已经知道用户名,即注册表的名称,所以让我们找到注册表的密码:

az acr credential show --name azacrcliregistry --resource-group az-acr-cli

输出将显示用户名和密码。请记下它们。您可以使用密码 1 或密码 2。既然我们确定了凭据,我们将通过执行以下操作登录到 Azure 容器注册表实例:

az acr login --name azacrcliregistry --username azacrcliregistry --password <password>

如果登录成功,您应该收到以下截图中显示的输出:

显示 Azure 容器注册表成功登录的输出

图 9.22:Azure 容器注册表登录成功

让我们继续推送一个镜像到注册表。为了推送一个镜像,首先我们需要有一个镜像。如果您正在使用在先前示例中使用的相同 VM,您可能已经拉取了一些镜像。如果镜像不存在,您可以使用docker pull <image name>来获取镜像。您可以使用docker images命令来验证可用镜像的列表。由于我们已经有了一个 nginx 镜像,我们不打算从 Docker Hub 拉取它。

现在我们有了镜像,让我们给它打标签。标记将帮助您知道您正在使用哪个镜像。例如,如果您有一个标记为v1的镜像,并对其进行了一些更改,您可以将其标记为v2。标记有助于您根据发布日期、版本号或任何其他标识符对镜像进行逻辑组织。我们需要以<AcrLoginName>/<image name>:<version tag>格式对镜像进行标记,其中acr-name是 Azure 容器注册表实例的 FQDN。要获取 Azure 容器注册表实例的 FQDN,请执行以下操作:

az acr show -n azacrcliregistry -g az-acr-cli | grep loginServer

对于 nginx 镜像,我们将其标记为nginx:v1

docker tag nginx azacrcliregistry.azurecr.io/ngnix:v1

让我们使用docker push命令将标记的镜像推送到 Azure 容器注册表:

docker push azacrcliregistry.azurecr.io/ngnix:v1

所有层都应该被推送,就像截图中显示的那样:

将标记的镜像推送到 Azure 容器注册表

图 9.23:将标记的镜像推送到容器注册表

假设您已经将多个镜像推送到 Azure 容器注册表,并希望获取所有镜像的列表。然后,您可以使用az acr repository list命令。要列出我们创建的 Azure 容器注册表实例中的所有镜像,请使用此命令:

az acr repository list --name azacrcliregistry -o table

您可以使用docker run命令来运行容器。但是请务必确保镜像名称的格式为<AcrLoginName>/<image>。Docker 时代即将结束,最终将被无守护进程的下一代工具所取代。

接下来的部分都是关于这些工具以及如何使用 Docker 创建类比进行平稳过渡的。

Buildah、Podman 和 Skopeo

在前面的部分中,我们讨论了 Docker 的工作原理以及如何使用它来部署容器。如前所述,Docker 使用 Docker 守护程序,这有助于我们实现所有这些。如果我们说人们已经开始向 Docker 告别,会怎么样?是的,随着下一代容器管理工具的推出,Docker 正在逐渐消失。我们并不是说 Docker 完全退出了舞台,但是它将被无根或无守护进程的 Linux 容器工具所取代。您没听错:这些工具没有守护进程在运行,使用单块守护进程的方法即将结束。难怪人们已经开始称使用这些工具部署的容器为“无 Docker 容器”。

历史

您可能会想知道这一切是什么时候发生的。早在 2015 年,Docker Inc.和 CoreOS 以及其他一些组织提出了“开放容器倡议”(OCI)的想法。这背后的意图是标准化容器运行时和图像格式规范。OCI 图像格式得到了大多数容器图像存储库的支持,例如 Docker Hub 和 Azure 容器注册表。现在大多数可用的容器运行时要么兼容 OCI,要么正在进行 OCI。这只是开始。

早些时候,Docker 是 Kubernetes 唯一可用的容器运行时。显然,其他供应商希望在 Kubernetes 中支持他们特定的运行时。由于这个困境和对其他供应商的支持不足,Kubernetes 在 2017 年创建了 CRI。CRI 代表容器运行时接口。您可以使用其他运行时,例如 CRI-O、containerd 或 frakti。由于 Kubernetes 的蓬勃发展以及对多个运行时的支持,Docker 的垄断地位开始动摇。在很短的时间内,Docker 的垄断地位发生了变化,并成为 Kubernetes 中支持的运行时之一。这种变化产生的涟漪实际上孕育了无守护进程工具的想法,并推翻了使用需要超级用户访问权限的单块守护进程的方法。

让我们尝试理解流行的术语,而不是使用通用术语。Buildah 用于构建容器,Podman 用于运行容器,Skopeo 允许您对存储图像的图像和存储库执行各种操作。让我们更仔细地看看这些工具。有些人建议在使用这些工具之前删除 Docker,但我们建议保留 Docker,以便您可以不断地将这些工具与其进行比较。如果您已经按照之前关于 Docker 的部分进行了操作,您将能够创建一个类比。

安装

安装这些工具非常简单。您可以在 Ubuntu 中使用 apt 或在 RHEL 中使用 yum 来安装这些工具。由于我们正在使用相同的 VM,我们将遵循这些软件包在 Ubuntu 上的安装。要安装 Buildah,请执行以下操作:

sudo apt update 
sudo apt install -y software-properties-common
sudo add-apt-repository -y ppa:projectatomic/ppa
sudo apt update 
sudo apt install -y buildah

由于在安装 Buildah 时已经添加了 PPA 存储库,我们可以直接使用apt install来部署 Podman。要安装 Podman,请执行以下操作:

sudo apt -y install podman

为了安装 Skopeo,我们需要在 Ubuntu VM 上安装snap。如果您使用的是 Ubuntu 16.04 LTS 或更高版本,则snap将默认安装。否则,您必须使用apt install snapd手动安装它。

让我们使用 snap 安装 Skopeo:

sudo snap install skopeo --edge

注意

如果您收到错误消息,指出“修订版不适用于生产”,您可以使用“--devmode”参数进行安装;这将跳过此错误并完成安装。

现在我们准备探索这些工具。

Buildah

在上一节中,我们讨论了 Dockerfile。这里有一个有趣的部分:Buildah 完全支持 Dockerfile。您所需要做的就是编写 Dockerfile 并使用bud命令,该命令代表使用 Docker 进行构建。让我们使用在 Dockerfile 部分中使用的相同示例。通过执行vi Dockerfile(您可以使用任何文本编辑器),添加以下行来创建一个 Dockerfile:

FROM nginx 
RUN apt-get --yes update
RUN apt-get --yes install apache2
CMD /usr/sbin/apachectl -e info -DFOREGROUND
EXPOSE 80

保存文件。

在构建之前,我们还需要处理其他事情。Buildah 会在/etc/containers/registries.conf文件中查找注册表列表。如果此文件不存在,我们需要创建一个,添加以下代码,并保存文件:

[registries.search]
registries = ['docker.io']

通过这样做,我们指示搜索 Docker Hub 的镜像。如果需要,您还可以将 Azure 容器注册表实例添加到列表中。

让我们继续构建镜像;确保您在 Dockerfile 所在的目录中。使用以下命令开始构建过程:

buildah bud -t ngnix-buildah .

我们创建了一个名为nginx-buildah的镜像。要查看镜像列表,可以使用buildah images命令。是的,我们知道它看起来与您在 Docker 中列出镜像的方式非常相似。我们需要牢记这个类比,它将帮助您学习。

输出将类似于此:

使用 buildah images 命令列出图像的输出

图 9.24:使用 buildah 命令列出图像

您可以看到 Buildah 列出了我们从 Docker Hub 拉取的图像,还列出了存储在本地主机存储库中的图像。

要从图像构建容器,我们可以使用以下命令:

buildah from <image>

这将创建一个名为<image>-working-container的容器。如果要构建一个 nginx 容器,请执行此操作:

buildah from nginx

您将获得类似于此的输出:

使用"buildah from nginx"命令构建 nginx 容器

图 9.25:构建 nginx 容器

就像使用docker ps列出所有容器一样,我们将运行buildah ps,我们将能够看到我们刚刚创建的nginx-working-container

使用 buildah ps 命令列出容器

图 9.26:使用 buildah ps 命令列出容器

此外,我们可以使用buildah run命令直接在容器中执行命令。语法如下:

buildah run <container name> <command>

让我们尝试打印我们创建的 nginx 容器的/etc/os-release文件的内容。命令如下:

buildah run nginx-working-container cat /etc/os-release

输出将类似于此:

使用 buildah run nginx-working-container cat /etc/os-release 命令打印 nginx 容器的内容

图 9.27:打印 nginx 容器的内容

与 Docker 一样,Buildah 支持pushpulltaginspect等命令。

Podman

我们通过 Buildah 构建的图像遵循 OCI 兼容性,并且可以与 Podman 一起使用。在 Podman 中,类比一直在继续;我们所要做的就是用 Podman 命令替换所有 Docker 命令。我们必须牢记的一个关键事项是,在 Podman 中,非 root 用户无法为容器进行端口绑定。如果您的容器需要端口映射,那么您必须以 root 身份运行 Podman。由于我们已经介绍了 Docker,您已经熟悉了 Docker 命令,我们将尝试运行一个容器并进行验证。让我们创建一个端口映射到8080的 nginx 容器。由于我们需要映射一个端口,我们将以sudo身份运行该命令:

sudo podman run -d -p 8080:80 --name webserver nginx

由于我们使用sudo命令创建了容器,因此它将归属于 root 用户。如果使用sudo创建容器,请确保您对与该容器相关的所有操作都链接了 sudo。

要列出容器,请使用podman ps,我们可以看到容器正在主机的0.0.0.0:8080上监听,并映射到容器的端口:

使用 podman ps 命令列出容器

图 9.28:使用 podman ps 命令列出容器

让我们进行一次curl调用,确认 Web 服务器是否在端口8080上运行:

curl localhost:8080

如果一切正常,您将能够看到 nginx 欢迎页面:

验证 Web 服务器 curl 命令的端口身份验证的输出

图 9.29:验证 Web 服务器端口身份验证

是的,容器正在无守护进程运行!

我们在这里不会覆盖所有 Podman 命令,一旦您熟悉了 Docker,您只需在命令行中用podman替换docker

Skopeo

如果您还记得,我们之前尝试使用 Docker 获取图像的标签。使用 Skopeo,您可以检查存储库、复制图像和删除图像。首先,我们将使用skopeo inspect命令在 Docker Hub 中获取图像的标签而不拉取它:

skopeo inspect docker://nginx:latest

运行此命令将触发一些警告。您可以忽略它们。如果您检查输出,您会看到它正在提供标签、层、操作系统类型等信息。

您可以使用skopeo copy命令在多个存储库之间复制容器图像。此外,您还可以将 Skopeo 与 Azure 容器注册表一起使用。

我们不会覆盖所有这些。但是,您可以访问这些工具的 GitHub 存储库:

容器和存储

本节旨在为您提供有关容器和存储的基本概念。每个可以创建镜像的构建工具都提供了向容器添加数据的选项。

您应该仅使用此功能来提供配置文件。尽可能将应用程序数据托管在容器之外。如果您想快速更新/删除/替换/扩展容器,如果数据在容器内,这几乎是不可能的。

当我们创建一个容器时,存储被附加到容器上。然而,容器是短暂的,这意味着当您销毁容器时存储也会被销毁。假设您为测试创建了一个 Ubuntu 容器,并且保存了一些在容器上测试并希望以后可以使用的脚本。现在,如果您意外删除了这个容器,那么您测试并保存以供以后使用的所有脚本都将消失。

您的应用程序数据很重要,您希望即使容器的生命周期结束后也能保留它。因此,我们希望将数据与容器的生命周期分离。通过这样做,您的数据不会被销毁,并且可以在需要时重复使用。在 Docker 中,可以通过使用卷来实现这一点。

Docker 支持各种持久卷的选项,包括 Azure Files。换句话说,您可以将 Azure 文件共享与 Docker 容器绑定为持久卷。为了演示这一点,我们将使用主机卷,其中一个位置将被挂载为容器的卷。这些步骤的目的是展示即使容器从主机中删除后数据仍然可以保存。

在创建容器时,卷信息通过-v参数传递给docker run命令。一般的语法如下:

docker run -v /some-directory/on host:/some-directory/in container

假设您有一个应用程序,将在容器中的/var/log目录中创建一个文件,并且我们需要使其持久化。在下一个命令中,我们将一个主机目录映射到容器的/var/log目录。

要完成这个练习,您需要在运行 Docker 的 Linux VM 上创建一个~/myfiles目录,该目录将映射到容器:

mkdir ~/myfiles

让我们创建一个带有交互式 shell 的 Ubuntu 容器,其中传递了-v参数以挂载卷:

docker run -it -v ~/myfile:/var/log ubuntu

如果容器成功创建,您将以 root 用户登录到容器中:

创建 Ubuntu 容器

图 9.30:创建 Ubuntu 容器

我们将转到容器的/var/log目录,并使用此命令创建 10 个空文件:

touch file{1..10}

列出目录的内容将显示我们刚刚创建的 10 个文件:

获取/var/log 目录中最近创建的 10 个文件的列表

图 9.31:列出/var/log 目录的内容

使用Ctrl + D退出交互式 shell,现在我们回到主机机器。现在我们将删除容器:

docker rm <id/name of the container>

id/name可以从docker ps --all命令的输出中获取。

现在容器已被删除,我们将转到主机机器的~/myfiles目录以验证内容。

在下面的屏幕截图中,您可以看到容器已成功删除;但是,~/myfiles目录仍然保存着我们在容器内创建的文件:

列出~/myfiles 目录中的文件

图 9.32:列出~/myfiles 目录中的文件

现在我们知道如何使我们的卷持久化。对于 Docker,有一些解决方案,比如github.com/ContainX/docker-volume-netshare

如果你正在使用 Docker 并想使用 Azure 文件,你可以使用 Cloudstor,这是一个可用的插件,网址是docs.docker.com/docker-for-azure/persistent-data-volumes

使用 Azure 文件存储可能不是最便宜的解决方案,但这样你可以获得所有你需要的可用性和备份选项。

如果你要使用 Kubernetes,那就是另一回事了。我们将在下一章中讨论这个问题。

总结

在本章中,我们讨论了在 Azure 中部署工作负载的另一种方式。在介绍容器虚拟化的历史、思想和概念之后,我们探讨了一些可用的选项。除了较旧的实现,如 LXC,我们还讨论了其他出色且稳定的容器托管实现:systemd-nspawn 和 Docker。

我们不仅看到了如何运行从仓库中拉取的现有镜像,还学会了如何创建我们自己的镜像。也许最大的好消息是,有一个名为 Buildah 的工具,它能够使用开放容器倡议(OCI)标准创建镜像,并且可以用于 Docker。

本章的大部分内容都是关于 Docker 的。迄今为止,这是最广泛实施的容器解决方案。而且,谈到实施,有许多实现/部署 Docker 的方法:

  • 在虚拟机中手动部署它

  • 从市场上部署一个准备好的虚拟机

  • Docker Machine

  • Azure 容器实例

还讨论了与 Docker Hub 和 Azure 容器注册表一起工作。

最后,我们讨论了 Buildah、Podman 和 Skopeo 等新的容器技术。

我们以几句话结束了本章关于容器和存储的讨论。如果容器被销毁,附加到容器的存储会发生什么,或者如何使存储持久化,你将在下一章第十章“使用 Azure Kubernetes 服务”中了解到。此外,我们还将讨论著名的容器编排工具 Kubernetes。

问题

  1. 使用容器的原因是什么?

  2. 容器不是你需要的解决方案的时候是什么情况?

  3. 如果你需要像虚拟私有服务器这样的东西,你想要一个虚拟机,还是有一个可用的容器虚拟化解决方案可能是个好主意?

  4. 为什么从一个解决方案(比如 Docker)迁移到另一个解决方案(比如 Buildah)不应该很困难?

  5. 开发机器用于什么?

  6. 为什么使用 Buildah 是一个好主意,即使它还在积极开发中?

  7. 为什么不应该将应用程序数据存储在容器中?

进一步阅读

在容器虚拟化领域进行进一步阅读并不是一件很容易的事情。对于systemd-nspawn来说,阅读起来相对容易:man 页面很容易理解。让我们提一个建议,这对于systemd-nspawn甚至 Docker 都是相关的:Red Hat 在他们的网站上提供了一份名为资源管理指南的文档(access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/resource_management_guide/),其中包含了关于 cgroups 的良好信息。

以下是有关 Docker 的一些参考资料:

  • 编排 Docker,作者 Shrikrishna Holla,你可以了解如何管理和部署 Docker 服务

  • 掌握 Docker 企业:敏捷容器采用的伴侣指南,作者 Mark Panthofer,你可以探索 Docker EE 的附加服务以及它们的使用方式

第十章:使用 Azure Kubernetes 服务。

在上一章中,我们探索了容器虚拟化的世界,特别是 Docker 容器。本章是关于使用Azure Kubernetes 服务AKS)管理容器化工作负载的。

本章与本书中的所有其他章节都不同。到目前为止,每一章都是关于基础设施和提供平台:经典的云系统管理员。甚至第九章Azure 中的容器虚拟化,也包含诸如“我们如何安装 Docker?”和“我们如何让容器运行起来?”的问题。本章将回答以下问题:

  • 我们在开发阶段和之后如何部署和管理我们的工作负载?

  • 我们如何进行扩展/缩减?

  • 可用性选项是什么?

Kubernetes 为所有这些问题提供了重要的答案。它是一个解决方案,用于自动化重要任务,如部署、管理、扩展、网络和容器化应用程序的可用性管理。

Kubernetes 最初是由 Google 设计的,现在由 Cloud Native Computing Foundation(www.cncf.io)维护。微软是这个基金会的重要合作伙伴,并且在金钱和代码方面是 Kubernetes 项目的重要贡献者。实际上,Kubernetes 的联合创始人之一 Brendan Burns 就在微软工作,并领导着在微软内部从事容器编排工作的团队。此外,微软还启动了几个针对 Kubernetes 的开源项目,提供了额外的工具。

由于微软在 Kubernetes 中的重要参与,它能够在 Azure 中实现一个完全与上游兼容的 Kubernetes 版本。这对开发人员也很重要,因此他们可以使用本地 Kubernetes 安装来开发软件,当开发完成后,将其发布到 Azure 云。

AKS 为 Kubernetes 提供了一个完全托管的容器即服务解决方案。这意味着您不必考虑 Kubernetes 软件的配置、管理和升级。控制平面由 Azure 管理。

AKS 使得在 Azure 中部署和管理 Kubernetes 变得容易:它可以处理从供应到保持应用程序最新和根据您的需求进行扩展的完整维护过程。

甚至在不中断的情况下升级 Kubernetes 集群的过程也可以通过 AKS 完成。

最后但同样重要的是,监控对于 Kubernetes 集群的每个部分都是可用的。

在本章结束时,您将能够:

  • 解释 Kubernetes 和 AKS 是什么。

  • 使用 AKS 来部署和管理您的集群。

  • 在 AKS 中维护应用程序的完整生命周期。

因此,在我们实际开始使用 AKS 之前,让我们首先了解技术要求是什么。

技术要求

正如本章介绍中所述,本章与所有其他章节都不同,这影响了技术要求。到目前为止,技术要求很简单:你只需要一堆虚拟机。

本章需要一个 DevOps 环境,在这个环境中,开发人员和运维人员在同一个团队中紧密合作,还有一个人既做开发又做运维相关的任务。

还必须做出选择:我们在哪里开发?本地还是在 Azure 云中?两者都可以,而且不应该有任何区别!从成本的角度来看,最好在工作站上进行。在本章中,假设您是在本地进行开发。因此,您需要一个工作站(或虚拟机)。我们需要以下内容:

  • Azure CLI。

  • Docker 和构建工具。

  • Kubernetes。

  • 一些基本的开发人员工具,比如 Git。

  • 一些其他工具,比如稍后介绍的 Helm。

  • 一个很好的集成开发环境IDE)。我们推荐使用 Microsoft Visual StudioVS)Code 以及 Docker 和 Kubernetes 的 Microsoft 扩展(仅当有图形界面时;否则使用 Nano 编辑器)。

  • 可选地,可以使用诸如 Ansible 之类的编排工具。请查看 Ansible azure_rm_aks8ks_raw模块。

使用 WSL 和 VS Code

您可以使用Windows 子系统WSL)和 VS Code 以及 VS Code 远程 WSL 扩展,在 Windows 桌面或笔记本电脑上获得 Linux 开发环境,而无需使用虚拟机。这将使您能够从 PowerShell 或 CMD 访问 Linux 文件,并从 Bash 访问 Windows 文件。VS Code 是一个可以在各种平台上运行并支持许多语言的源代码编辑器。您可以使用 WSL 和 VS Code 从您喜欢的 Windows 平台开发、运行和调试基于 Linux 的应用程序。可以使用 PowerShell 启用 WSL 功能,并从 Microsoft Store 安装 Linux。VS Code 适用于 Windows 和 Linux,并可从code.visualstudio.com/下载。由于 VS Code 的配置设置在 Windows 和 Linux 平台上都是保持一致的,因此您可以轻松地在 Windows 和 Linux 之间切换。

您可以在docs.microsoft.com/en-us/learn/modules/get-started-with-windows-subsystem-for-linux/找到 WSL 的逐步教程,并在docs.microsoft.com/en-us/windows/wsl/install-win10找到详细的安装指南。在 Windows 上运行时,您可以配置默认 shell 并在 PowerShell 和 WSL 之间进行选择,在 Linux 上可以选择 Zsh 或 Bash。

安装依赖项

我们将使用 Ubuntu 18.04 LTS 桌面版。但是您也可以在 Azure 虚拟机中使用 Ubuntu 18.04 LTS 服务器。有了其他章节中获得的所有知识,很容易将我们将要做的事情转移到其他 Linux 发行版、macOS 甚至 Windows 上:

  1. 首先,升级 Ubuntu:
sudo apt update &&sudo apt upgrade
  1. 安装开发人员工具,包括其他一些依赖项和openssh
sudo apt install build-essential git curl openssh-server \
ebtablesethtoolsocat
  1. 首先,我们将安装 Azure CLI。

您可以通过运行单个命令安装 Azure CLI:

curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash 

或者,您可以使用以下说明进行手动安装。

获取所需的软件包:

sudo apt-get install ca-certificates curl apt-transport-https lsb-release gnupg

获取并安装签名密钥:

curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | 
sudo tee /etc/apt/trusted.gpg.d/microsoft.asc.gpg> /dev/null
sudo apt-add-repository \
  https://packages.microsoft.com/repos/azure-cli
curl -L https://packages.microsoft.com/keys/microsoft.asc \
  | sudo apt-key add -
sudo apt update 
sudo apt install azure-cli
  1. 要安装 PowerShell 和 VS Code,我们使用的是 snaps,这是类似于 Windows 上的便携式应用程序的通用软件包:
sudo snap install --classic powershell
sudo snap install --classic vscode

或者,您可以使用以下命令安装 PowerShell Core:

curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
curl https://packages.microsoft.com/config/ubuntu/18.04/prod.list | sudo tee /etc/apt/sources.list.d/microsoft.list
sudo apt update
sudo apt install -y powershell
  1. 键入pwsh以启动 PowerShell Core:
admin123@kubes:~$ pwsh

如果 PowerShell Core 成功启动,您将获得以下输出:

使用 pwsh 命令启动 PowerShell Core

图 10.1:启动 PowerShell Core
  1. 安装 Azure 的 Azure cmdlet:
sudo pwsh -Command "Install-Module PowerShellGet -Force"
sudo pwsh -Command "Install-Module -Name AzureRM.Netcore \
 -AllowClobber"
sudo chown -R $USER ~/.local/
  1. 安装 Docker:
curl -sSL https://get.docker.com/ | sudo sh
sudo usermod -aG docker $USER

您将获得 Docker 版本详细信息如下:

获取 Docker 版本详细信息

图 10.2:Docker 版本详细信息
  1. 暂时停止 Docker:
Sudo systemctl stop docker.service

kubectl 安装

kubectl 是一个命令行界面,可用于管理 Kubernetes 集群。它可用于许多操作。例如,使用kubectl create创建一个或多个文件,并使用kubectl delete从文件中删除资源。我们将使用 Azure CLI 来安装kubectl,并以 root 身份执行以下命令以授予所需的权限:

sudo -i
az login
az aks install-cli

首先,您需要使用以下命令下载最新版本:

curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.16.3/bin/linux/amd64/kubectl

接下来,使其可执行:

chmod +x ./kubectl

现在,将其移动到您的PATH

Sudo mv ./kubectl /usr/local/bin/kubectl

通过请求版本信息来验证安装:

kubectl version

要启用自动补全,可以为 Bash 和 Zsh 在kubectl中执行以下命令:

kubectl completion bash > ~/.kube/completion.bash.inc
printf"
 # Kubectl shell completion
 source '$HOME/.kube/completion.bash.inc'
">> $HOME/.bash_profile
source $HOME/.bash_profile

对于 Zsh,请执行以下命令:

sudo -i
kubectl completion zsh>"${fpath[1]}/_kubectl"
exit
source <(kubectl completion zsh)

到目前为止,我们已经使用curl命令在 Linux 上安装了最新版本的 kubectl 二进制文件,并启用了 kubectl 的 shell 自动补全。现在我们准备使用 AKS 了。

注意

如果你使用 kubectl 时收到类似Error from server (NotAcceptable): unknown (get nodes)的错误消息,使用https://dl.k8s.io/v1.10.6/kubernetes-client-linux-amd64.tar.gz降级你的客户端。

尽管这完全超出了本书的范围,但我们个人喜欢使用 Zsh shell,并使用一个名为 Spaceship 的漂亮定制。提示符可以让你更清楚地了解你在哪里以及在处理 AKS 时在做什么。

这是快速安装:

sudo apt install zshnpm fonts-powerline
zsh # and create a .zshrc file with option 0 
npm install spaceship-prompt
chsh -s /bin/zsh

开始使用 AKS

Azure AKS 使得部署和管理容器应用变得容易。你可以快速定义、部署和调试 Kubernetes 应用程序,还可以使用 Azure AKS 自动将应用程序容器化。你可以自动化监控、升级、修复和扩展,从而减少手动基础设施维护。安装了 kubectl 后,现在是时候在 Azure 中设置和探索 Kubernetes 环境了:

  1. 创建一个集群。

  2. 查找有关集群的信息。

  3. 部署一个简单的工作负载。

使用 Azure CLI 创建一个集群

在 Kubernetes 中,我们将使用集群。一个集群包含一个主节点或控制平面,它控制着一切,以及一个或多个工作节点。在 Azure 中,我们不需要关心主节点,只需要关心节点。

为了本章的目的,最好为其创建一个新的资源组:

az group create --location eastus--name MyKubernetes

在这个资源组中,我们将部署我们的集群:

az aks create --resource-group MyKubernetes \
  --name Cluster01 \
  --node-count 1 --generate-ssh-keys 

这个命令可能需要长达 10 分钟的时间。一旦你收到提示,用以下方法验证一下:

az aks list

在输出中,你会找到很多信息,比如完全合格的域名,集群的名称等等:

获取已部署集群的详细信息

图 10.3:部署集群的详细信息

有一个名为 Kubernetes Dashboard 的 Web 界面可供使用,你可以用它来访问集群。要使其可用,执行以下操作:

az aks browse --name Cluster01 --resource-group MyKubernetes

将你的浏览器指向http://127.0.0.1:8001

Kubernetes Dashboard 显示有关集群和资源组的详细信息

图 10.4:Kubernetes Dashboard

az实用程序正在将门户隧道传输到你的本地主机。按Ctrl + C退出隧道。

为了能够使用kubectl实用程序,我们需要将配置合并到本地配置文件中:

az aks get-credentials --resource-group MyKubernetes \
 --name Cluster01

上述命令的输出如下:

使用 az aks get-credentials 命令将配置合并到本地配置文件

图 10.5:将配置合并到本地配置文件

由于我们时髦的命令提示符,你可以看到我们从本地 Kubernetes 集群切换到了 Azure 中的集群。要查看可用的集群,执行以下操作:

kubectl config get-contexts

上述命令的输出如下:

使用 kubectl config get-contexts 命令查看可用的集群

图 10.6:查看可用的集群

你可以使用kubectl config use-context <cluster>切换到另一个集群。

你也可以使用kubectl找到有关你的集群的信息:

kubectl cluster-info

上述命令的输出如下:

使用 kubectl cluster-info 命令获取有关集群的详细信息

图 10.7:有关集群的信息

我们在这里创建了一个名为Cluster01的 Kubernetes 集群,使用了az aks create命令。现在让我们列出节点,这些节点是 Kubernetes 的工作机器,并由主节点管理:

kubectl get nodes

上述命令的输出如下:

使用 kubectl get nodes 命令列出节点

图 10.8:列出节点

在 AKS 中的第一个部署

AKS 允许您构建和部署应用程序到托管的 Kubernetes 集群中,该集群管理容器化应用程序的连接和可用性。您可以使用简单的 kubectl create 命令在 AKS 中部署 Docker 容器:

Kubectl createnginx --image=nginx --port=80

几秒钟内,会出现消息:deployment.apps/nginx created

使用以下命令验证部署:

kubectl get deployment

上述命令的输出如下:

使用 kubectl get deployment 命令验证部署

图 10.9:验证镜像部署

当我们执行 run 命令时,Docker 容器被部署到了集群中。更具体地说,一个 pod 被创建,并在其中运行容器。一个 pod 是一组共享资源的容器,比如存储和网络资源,它还包含了如何运行容器的规范。要查看创建的 pod,请执行以下命令:

kubectl get pods

上述命令的输出返回了 pod 名称、pod 状态(运行中、挂起、成功、失败或未知)、重启次数和正常运行时间,如下所示:

使用 kubectl get pods 命令获取 pod 的详细信息

图 10.10:pod 的详细信息

Pods 来来去去;它们是动态创建的,可以在扩展上/下进行。使用 explain 命令,您可以找到有关 pod 的各种信息:

kubectl explain pods/nginx-57867cc648-dkv28

让我们删除 pod:

kubectl delete pod nginx-57867cc648-dkv28 

再次执行 kubectl get pods 命令;您应该会看到一个新的 pod 可用。

创建服务

但实际上,您不应该关心 pod:服务才是重要的。服务是使应用程序对外界可访问的对象。在服务的背后,有一个或多个 pod。服务跟踪 pod 及其 IP 地址,并且它是一组逻辑 pod 及其策略的抽象。您可以使用以下命令列出命名空间中的所有服务:

kubectl get services

上述命令的输出如下:

使用 get services 命令列出命名空间中的所有服务

图 10.11:列出命名空间中的所有服务

只找到一个服务,CLUSTER-IP。可以使用以下命令找到更多详细信息:

kubectl describe services/kubernetes

获取 Kubernetes 中服务的描述

图 10.12:获取 Kubernetes 服务的描述

让我们摆脱我们的第一个部署:

kubectl delete deployment nginx

使用 kubectl delete deployment nginx 命令删除第一个部署

图 10.13:删除第一个部署

让我们创建一个新的:

kubectl run nginx --image=nginx

为 nginx 创建一个新镜像

图 10.14:创建一个新的 nginx 镜像

请注意,我们没有暴露端口。让我们使用 kubectl get pods 命令列出 pod。为了使资源可访问,我们添加了一个 LoadBalancer 类型的服务:

kubectl expose pod <pod name> --port=80 --target-port=80 \
  --type=LoadBalancer

输出应该类似于以下内容:

列出 pod 并添加 LoadBalancer 类型的服务

图 10.15:列出 pod 并添加 LoadBalancer 类型的服务

在浏览器中使用 EXTERNAL-IP 地址。它会显示 nginx 的欢迎页面。

多容器 pod

一个 pod 也是 Kubernetes 用来维护容器的抽象层。有许多用例和真实场景需要在单个 pod 中有多个容器,以支持微服务容器应用程序之间的通信,如下图所示。此图中的持久存储显示了每个容器在 pod 的生命周期中进行读写操作的通信方式,当您删除 pod 时,共享的持久存储数据会丢失:

一个块图表描述多容器 pod 的架构

图 10.16:多容器 pod 的架构

但是有一些用例是基于 pod 为 pod 内的容器提供共享资源的事实,比如:

  • 带有辅助应用程序(如日志记录和监控)的容器

  • 反向代理

到目前为止,我们使用—image参数创建了一个简单的 pod。对于更复杂的 pod,我们需要以 YAML 格式进行规范。创建一个名为myweb.yaml的文件,内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: myweb
spec:
restartPolicy: Never
  volumes:
  - name: logger
emptyDir: {}
  containers:
  - name: nginx
    image: nginx
volumeMounts:
    - name: logger
mountPath: /var/log/nginx
readOnly: false
  - name: logmachine
    image: ubuntu
volumeMounts:
    - name: logger
mountPath: /var/log/nginxmachine

在这个文件中,创建了一个名为journal的共享卷。emptydir指令确保在创建 pod 时创建卷。

验证,执行以下命令:

kubectl exec myweb -c nginxfindmnt | grep logger

这个命令在myweb pod 中的nginx容器上执行findmnt命令。我们已经创建了容器、pod 和共享存储。现在让我们把注意力转移到 Helm 上,它是 Kubernetes 的包管理器。

注意

前面的选项不能作为集群解决方案使用,您可能需要使用mountOptions标志将其中一个容器的文件系统挂载为只读。

使用 Helm 工作

Helm (helm.shgithub.com/helm)是 Kubernetes 的应用程序包管理器。您可以将其与 Linux 的aptyum进行比较。它帮助使用图表管理 Kubernetes,这些图表定义、安装和升级您想要部署在 Kubernetes 上的应用程序。

Helm 的 GitHub 仓库和 Microsoft 提供了许多图表,Microsoft 是该项目最大的贡献者之一。

安装 Helm

如果您使用 Ubuntu 系统,有两种选择——您可以使用snap包安装 Helm,或者只需从github.com/kubernetes/helm/releases下载二进制文件。使用二进制文件适用于每个 Linux 发行版,而snap存储库并不总是有 Helm 的最新版本。因此,让我们使用github.com/helm/helm/releases找到 Helm 的最新版本,并相应更改helm-vx.x.x-linux-amd64.taz.gz文件名中的x

cd /tmp
wget https://storage.googleapis.com/kubernetes-helm/\
  helm-v2.9.1-linux-amd64.tar.gz
sudo tar xf helm-v2.9.1-linux-amd64.tar.gz --strip=1 -C \
  /usr/local/bin linux-amd64/helm

始终在网站上检查最新版本,并相应更改命令。

macOS 用户可以使用 Brew (brew.sh/):

brew install kubernetes-helm

客户端已安装,有了这个客户端,我们可以将服务器部分 Tiller 部署到我们的 Kubernetes 集群中:

helm init

使用 helm init 命令将 Tiller 部署到 Kubernetes 集群中

图 10.17:将 Tiller 部署到 Kubernetes 集群中

验证版本:

helm version

输出应该类似于以下内容:

验证 Helm 版本

图 10.18:验证 Helm 版本

为了让 Helm 能够访问 Kubernetes 集群,必须创建一个带有相应角色的服务账户:

kubectl create serviceaccount \
  --namespace kube-system tiller

如下截图所示,我们使用kubectl create命令在kube-system命名空间中创建了 Tiller 服务账户:

在 kube-system 命名空间中创建 Tiller 服务账户

图 10.19:在 kube-system 命名空间中创建 Tiller 服务账户

授予 Kubernetes 资源的集群管理员访问权限以执行管理任务:

kubectl create clusterrolebinding tiller-cluster-rule \
  --clusterrole=cluster-admin \
  --serviceaccount=kube-system:tiller

如下截图所示,您可以根据自己的需求创建自定义角色:

使用 kubectl create clusterrolebinding 命令创建基于自定义角色

图 10.20:创建自定义角色

Helm 是安装在本地计算机上的客户端,Tiller 是安装在 Kubernetes 上的服务器。要重新配置 Helm,即确保 Tiller 的版本与本地 Helm 匹配,执行:

helm init --service-account tiller --upgrade

Helm 仓库管理

Helm 仓库是一个 HTTP 服务器,可以提供 YAML 文件,并包含托管在同一服务器上的打包图表和index.yml。在安装期间添加了两个仓库:

让我们从 Microsoft 添加仓库:

helm repo add azure \
  https://kubernetescharts.blob.core.windows.net/azure

添加来自 Microsoft 的仓库

图 10.21:从 Microsoft 添加存储库

检查可用的存储库:

helm repo list

输出应类似于以下内容:

使用 helm repo list 命令检查可用的存储库

图 10.22:检查可用的存储库

要更新存储库信息,请执行以下操作:

helm repo update

您还可以使用remove参数删除存储库。

使用 Helm 安装应用程序

让我们看看存储库中有什么可用的内容:

helm search wordpress

前面命令的输出如下:

关于 wordpress 的搜索结果,提供有关聊天版本、应用程序版本、描述等详细信息。

图 10.23:搜索 wordpress 存储库

如果您想要有关图表的信息,如如何使用它、可用参数等,可以使用helm inspect命令。现在,我们只是要部署它:

helm install stable/wordpress

前面命令的安装输出日志包含访问WordPress实例所需的必要详细信息。

使用以下命令验证集群中 Helm 图表的状态:

helm ls

前面命令的输出返回修订名称、更新时间戳、状态、图表及其命名空间等信息:

使用 helm ls 命令验证 Helm 图表的状态

图 10.24:验证 Helm 图表的状态

审查安装过程的先前输出:

helm status contrasting-chicken

该命令返回部署时间戳、命名空间和状态,以及资源详细信息,如v1/PersistentVolumeClaimv1/Serviceextensions/Deploymentv1/Secret以及数据库服务器的connection详细信息:

使用 helm status 命令审查 helm 状态

图 10.25:审查 helm 状态

当然,kubectl也会向您显示以下结果:

使用 kubectl 获取部署详细信息

图 10.26:使用 kubectl 获取部署详细信息

以下截图显示了kubectl get service命令的输出:

kubectl get service 命令的输出

图 10.27:kubectl get service 命令的输出

让我们删除我们的部署(名称可以使用helm ls找到):

helm delete <NAME>

使用 helm delete 命令删除部署

图 10.28:使用 helm delete 命令删除部署

要自定义应用程序,请执行以下操作:

helm inspect stable/wordpress

然后,搜索 WordPress 设置:

搜索 WordPress 设置

图 10.29:搜索 WordPress 设置

创建一个 YAML 文件,例如custom.yaml,其中包含以下内容:

image:
  registry: docker.io
  repository: bitnami/wordpress
  tag: 4-ol-7
wordpressUsername: linuxstar01
wordpressEmail: linuxstar01@example.com
wordpressFirstName: Kamesh
wordpressLastName: Ganesan
wordpressBlogName: Linux on Azure – 2nd Edition!

然后,部署 WordPress 应用程序:

helm install stable/wordpress -f custom.yaml

您可以使用kubectl命令验证结果。首先,获取 Pod 的名称:

kubectl get pod

使用 kubectl get pod 命令验证 WordPress 应用程序的部署

图 10.30:验证 WordPress 应用程序的部署

之后,执行以下操作:

kubectl describe pod <podname>

使用 kubectl describe pod 命令获取 pod 的描述

图 10.31:获取 pod 描述

例如,在“事件”部分,您会看到拉取了docker.io/bitnami/wordpress:4-ol-7镜像。

清理一切:

helm delete stable/wordpress
kubectl scale sts --all --replicas=0
kubectl delete pod --all
kubectl delete sts --all --cascade=false

不要担心有状态的集合(sts);它们是由该应用程序创建的,用于有序部署和共享持久存储。

创建 Helm 图表

Helm 图表类似于 Linux 发行版中使用的软件包,您可以使用 Helm 客户端浏览软件包存储库(图表)目录结构。有许多为您创建的图表,也可以创建自己的图表。

首先,创建一个工作目录,并准备好使用:

helm create myhelm
cd myhelm

前面的命令应该给出类似的输出:

创建一个工作目录,并通过首先运行 cd myhelm 然后执行 ls -al 命令使其准备好使用

图 10.32:创建工作目录

创建了一些文件和目录:

  • Chart.yaml文件:该文件包含有关图表的基本信息。

  • values.yaml文件:默认配置值。

  • charts目录:依赖图表。

  • templates目录:用于为 Kubernetes 创建清单文件

此外,您还可以添加一个LICENSE文件,一个README.md文件和一个带有要求的文件,requirements.yaml

让我们稍微修改Chart.yaml

apiVersion: v1
appVersion: 1.15.2
description: My First Nginx Helm
name: myhelm
version: 0.1.0
maintainers:
- name: Kamesh Ganesan
    email: kameshg@example.com
    url: http://packtpub.com

该文件或多或少是自解释的:维护者是可选的。appVersion是指,在这个例子中,nginx 的版本。

使用以下命令验证配置:

helm lint

花些时间来调查templates目录和value.yaml文件中的文件。当然,我们之所以使用 nginx 作为示例,是因为helm create创建的文件也使用 nginx 作为示例。

首先,执行干运行:

helm install --dry-run --debug ../myhelm

这样,您就可以看到将用于部署应用程序的清单。之后,您就可以安装它了:

helm install ../myhelm

安装后,我们意识到在干运行时,有一些不对劲:nginx 的版本是nginx: stable,即版本 1.14.0。打开values.yaml文件,将tag: stable更改为tag: 1.15.2

使用helm ls查找名称并更新它:

helm upgrade <name> ../myhelm

将创建一个新的 pod;旧的将被删除:

通过将新的 pod 替换为旧的 pod 来查找 pod 的名称并更新它的输出

图 10.33:更新 pod 版本

甚至有一个rollback选项,如果您想恢复到旧版本:

helm rollback <RELEASE> <REVISION>

您只需要指定要恢复的发布和修订版本。

使用草稿

作为开发人员,您通常会使用 Helm,用于准备就绪的应用程序,并且应该进行维护。您很可能还将代码托管在 GitHub 等版本控制系统上。

这就是草稿(github.com/Azure/draft)的用武之地。它试图简化流程,从您的代码开始,在 Kubernetes 集群中进行。

该工具正在大力开发中。草稿变得越来越受欢迎和稳定,定期添加新的语言和功能。

如果开发阶段变成了似乎可用的东西,您仍然可以使用草稿,但更有可能的是您也会转向 Helm。

要了解草稿支持哪些编程语言,您可以在安装后执行以下命令:

draft pack list
Available Packs:
  github.com/Azure/draft/clojure
  github.com/Azure/draft/csharp
  github.com/Azure/draft/erlang
  github.com/Azure/draft/go
  github.com/Azure/draft/gradle
  github.com/Azure/draft/java
  github.com/Azure/draft/javascript
  github.com/Azure/draft/php
  github.com/Azure/draft/python
  github.com/Azure/draft/ruby
  github.com/Azure/draft/rust
  github.com/Azure/draft/swift

安装草稿

要使用草稿,必须安装和配置 Helm。

github.com/Azure/draft/releases获取您的副本:

cd /tmp
wget https://azuredraft.blob.core.windows.net/draft/\
  draft-v0.15.0-linux-amd64.tar.gz
sudo tar xf draft-v0.15.0-linux-amd64.tar.gz --strip=1 \
  -C /usr/local/bin linux-amd64/draft

始终在网站上检查最新版本,并相应更改命令。

macOS 用户可以使用 Brew 安装它:

brew tap azure/draft && brew install draft

您可以看到,使用 Helm 的开发人员也参与了 Draft 的开发。在这两种情况下,其中许多人是微软的开发人员。与 Helm 类似,在安装客户端后,您必须初始化草稿:

draft init

这将安装一些默认插件并设置您可以在草稿中使用的存储库。

使用以下命令检查版本:

draft version

在撰写本文时,其版本为 0.16.0:

输出显示草稿版本为 0.16.0

图 10.34:检查草稿版本

最后一步涉及配置 Docker 存储库、Docker Hub 或 Azure。在本书的目的中,我们使用的是 Azure。

创建Azure 容器注册表ACR):

az acr create --resource-group MyKubernetes --name LinuxStarACR --sku Basic

登录到LinuxStarACR

az acr login --name LinuxStarACR

使用 az acr login --name LinuxStarACR 命令登录到 LinuxStarACR

图 10.35:登录到 LinuxStarACR

配置存储库:

draft config set registry LinuxStarACR

登录到注册表:

az acr login --name LinuxStarACR

在草稿和 ACR 之间创建信任:

export AKS_SP_ID=$(azaks show \
 --resource-group <resource group> \
 --name <Kubernetes Cluster>
 --query "servicePrincipalProfile.clientId" -o tsv)
export ACR_RESOURCE_ID=$(azacr show \
 --resource-group <resource group>\
 --name <ACR Name> --query "id" -o tsv)
az role assignment create --assignee $AKS_SP_ID --scope $ACR_RESOURCE_ID --role contributor 

我们已成功安装了 Draft v0.16.0 并创建了 ACR。最后,我们在 Draft 和 ACR 之间创建了信任。现在是时候继续开始使用草稿了。

使用草稿

让我们开发一些简单的 Draft 代码。为此,我们将创建一个名为mynode的目录。在这个目录中,我们将创建一个名为mynode.js的文件,其中包含以下代码:

var http = require('http');
var server = http.createServer(function(req, res) {
res.writeHead(200);
res.end('Hello World!');
});
server.listen(8080);

这是一个简单的 Web 服务器,提供一个显示Hello World!的页面。我们处于开发过程的早期阶段。要创建一个package.json文件,请执行以下操作:

npminit

填写信息:

name: (mynode)
version: (1.0.0) 0.0.1
description: My first Node App
entry point: (mynode.js)
test command: node mynode.js
git repository:
keywords: webapp
author: Kamesh Ganesan
license: (ISC)

现在我们准备执行 Draft:

draft create

使用 draft create 命令创建 Dockerfile

图 10.36:使用 draft create 命令创建 Dockerfile

这将创建一个 Dockerfile 和所有 Helm 的信息。

输出的最后一行“准备启航”实际上意味着你已经准备好执行:

draft up

上述命令生成以下输出:

使用 draft up 命令构建和推送 Docker 镜像

图 10.37:构建和推送 Docker 镜像

这将构建镜像并发布应用程序。

执行helm ls将显示mynode应用程序:

显示 mynode 应用程序的详细信息的输出

图 10.38:获取 mynode 应用程序的详细信息

使用kubectl get services来显示服务:

使用 kubectl get services 命令显示服务

图 10.39:使用 kubectl get services 显示服务

这里一切似乎都很正常,但kubectl get pod告诉我们情况并非如此:

使用 kubectl get pod 命令检查 pod 的状态

图 10.40:检查 pod 的状态

draft logs命令没有显示任何错误。因此,让我们看看 Kubernetes 认为什么:

kubectl logs <Pod Name>

它声明npm ERR! missing script: start。故意在package.json文件中制造了一个错误。根据以下示例修改内容,修改值:

{
"name": "mynode",
"version": "0.0.2",
"description": "My first Node App",
"main": "mynode.js",
"scripts": {
"start": "node mynode.js",
"test": "echo \"Error: no test specified\"& exit 1"
  },
"keywords": [
"webapp"
  ],
"author": "Kamesh Ganesan",
"license": "ISC"
}

通过再次执行以下操作来更新应用程序:

draft update

连接到应用程序:

draft connect

使用 draft connect 命令连接应用程序

图 10.41:连接到应用程序

打开另一个终端:

curl localhost:39053

输出必须是Hello World!

在终端中按下Ctrl + C,运行draft connect,并删除部署:

draft delete

使用kubectl get all检查集群资源,并根据需要进行清理。

管理 Kubernetes

我们已经创建了一个 Kubernetes 集群,并且学习了kubectl实用程序以及一些可用于开发和维护 Kubernetes 集群中应用程序的工具。

因此,如果回顾一下本章开头的三个问题,我们已经回答了第一个问题。在本节中,我们将回答另外两个问题,并介绍如何更新 Kubernetes 版本。

更新应用程序

早些时候,我们使用 Helm 和 Draft 来管理我们的应用程序,这意味着所有的辛苦工作都已经为我们完成。但是你也可以借助kubectl来更新工作负载。

通常情况下,我们的集群现在应该是空的,所以让我们快速再次部署我们的nginx pod:

kubectl run nginx --image=nginx

仔细查看部署:

显示 nginx pod 部署成功的输出

图 10.42:部署 nginx pod

这实际上告诉我们,我们想要一个实例,有一个正在运行,它是最新的(已更新以匹配所需容量的实例数),并且它是可用的。运行的 nginx 版本不是最新的,所以我们想要将其更新到版本 1.17.5。执行以下操作:

kubectl edit deployment/nginx

将镜像更改为nginx:1.17.5

将镜像更改为 nginx:1.17.5

图 10.43:将镜像更改为 nginx:1.17.5

kubectl rollout命令可用于管理资源部署。一些有效的 rollout 选项包括 status、history、pause、restart、resume 和 undo。kubectl rollout status显示部署的当前状态,而kubectl rollout history列出以前的修订和配置。

kubectl rollout status deployment nginx
kubectl rollout history deployment nginx

或者,更好的是,您可以使用describe命令,它为您提供比前两个命令结合起来更详细的输出:

kubectl describe deployment nginx

使用 kubectl describe deployment 命令获取 nginx 部署的更详细输出

图 10.44:nginx 部署的详细信息

更新部署的另一种方法是使用set image命令,通过更新映像来部署更新的 nginx 容器,新版本为 1.17.5,如下所示:

kubectl set image deployment/nginxnginx=nginx:1.17.5 --record

如前面的屏幕截图所示,nginx 容器映像已成功升级为版本 1.17.5。

扩展应用程序

目前有一个正在运行的 pod,但为了处理所有传入的负载,您可能需要更多实例并且需要负载平衡传入的流量。为此,您需要副本来定义任何给定时间运行的指定数量的 pod 副本。

让我们回到kubectl并获取当前的部署:

获取当前部署的状态

图 10.45:获取当前部署的状态

此刻的期望(配置)状态是1。当前情况是1,有1个可用。

要扩展到三个实例,请执行以下操作:

kubectl scale deployment nginx --replicas=3

再次运行kubectl get deployments;然后查看可用的 pod:

kubectl get pods -o wide

显示扩展后可用 pod 的状态的输出

图 10.46:扩展后检查可用的 pod

创建负载均衡器服务:

kubectl expose deployment nginx --type=LoadBalancer \
 --name=nginx-lb --port 80
kubectl get services

显示创建负载均衡器服务的输出

图 10.47:创建负载均衡器服务

现在每个 HTTP 请求都由负载均衡器处理,并且流量分布在实例之间。

您还可以使用自动缩放。首先,安装 Metrics Server:

git clone https://github.com/kubernetes-incubator/metrics-server.git 
kubectl create -f metrics-server/deploy/1.8+/

配置自动缩放:如果负载超过50%,则会创建额外的实例,最多为10

kubectl autoscale deployment nginx --cpu-percent=50 --min=3 --max=10

当然,在这种情况下,至少有两个节点在您的集群中是有意义的:

azaks scale --name Cluster01 \
  --resource-group MyKubernetes \
  --node-count 2
kubectl get nodes

请注意,此过程大约需要 10 分钟。要查看自动缩放的状态,请执行以下操作:

kubectl get hpa

使用 kubectl get hpa 命令查看自动缩放的状态

图 10.48:列出自动缩放器

升级 Kubernetes

与任何软件或应用程序一样,您需要定期升级 Kubernetes 集群以使其保持最新状态。升级非常重要,可以获得最新的错误修复和所有关键的安全功能,以及最新的 Kubernetes 功能。如果要在不间断的情况下升级 Kubernetes 控制平面,则还需要有多个可用节点。以下步骤将向您展示如何快速升级 Kubernetes 集群。

首先查看当前版本:

az aks list --query "[].kubernetesVersion"

显示当前 Kubernetes 版本为 1.13.12 的输出

图 10.49:查看当前 Kubernetes 版本

询问您位置的可用版本:

az aks get-versions --location eastus --output table | egrep "¹.13.12"

获取 East US 位置可用版本的输出

图 10.50:East US 位置的可用版本

我们可以升级到版本 1.14.8:

az aks upgrade --resource-group MyKubernetes
  --name Cluster01 \
  --kubernetes-version 1.14.8 --yes --no-wait

添加--no-wait参数的效果是您几乎立即就能恢复提示符。

这样,大约 3 分钟后,您可以开始使用kubectl来获取节点和 pod 的状态(使用-owide参数,例如kubectl get pods -o wide),并了解已创建具有最新版本的新节点。工作负载在该节点上重新创建,并更新了另一个节点。之后,最后一个剩下的节点被清空并升级。

持久存储

在上一章中,我们提到了在容器中使用持久存储的多种方法,并且在本章中也提到了这一点。

Kubernetes 可以配置持久存储,但您必须提供它,例如通过 NFS 容器或通过实施 StorSimple iSCSI 虚拟阵列(如果您需要从多个容器进行读/写访问,这将特别有用)。即使您使用 Azure 存储,也有许多选择要做。您想使用磁盘还是 Azure 存储?您想动态创建它们还是使用现有的(静态)?大多数这些问题都是基于成本和对复制、备份和快照等服务需求来回答的。

在本节中,我们想要涵盖动态选项;在编排方面,这是一个更好的选择,因为您可以在 Kubernetes 内部完成所有操作(或使用其周围的工具)。

无论您使用 Azure 存储还是磁盘,您都需要在与 Kubernetes 相同的资源组中拥有一个存储账户:

az storage account create --resource-group MyKubernetes \
 --name mystorageest1 –sku Standard_LRS

请查看第二章开始使用 Azure 云,以获取上述命令的语法。请记住,名称必须是唯一的。

Kubernetes 的 Azure 磁盘

您可以动态或静态地为在 AKS 集群中的一个或多个 Kubernetes pod 使用的持久卷提供存储类。有两种存储类:标准 Azure 磁盘(默认)和高级 Azure 磁盘,这是一种托管的高级存储类:

  1. 首先,创建一个 YAML 文件来创建存储类。这样可以自动提供存储:
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: storageforapp
provisioner: kubernetes.io/azure-disk
parameters:
storageaccounttype: Standard_LRS
 location: eastus
 kind: shared
  1. 使用以下内容应用它:
kubectlapply -f storageclass.yaml

用刚创建的文件名替换文件名。

  1. 还需要另一个 YAML 文件来索赔持久卷,或者换句话说,创建它:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: claim-storage-for-app
  annotations:
volume.beta.kubernetes.io/storage-class: storageforapp
spec:
accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  1. 请注意,匹配是在注释中完成的。也要应用这个文件:
kubectlapply -f persistentvolume.yaml
  1. 使用以下内容验证结果:
kubectl get sc

执行 kubectl get sc 命令以验证存储类的创建

图 10.51:验证存储类的创建
  1. 要在 pod 中使用存储,可以像以下示例一样使用它:
kind: Pod
apiVersion: v1
metadata:
  name: my-web
spec:
  containers:
    - name: nginx
      image: nginx
volumeMounts:
      - mountPath: "/var/www/html"
        name: volume
  volumes:
    - name: volume
persistentVolumeClaim:
claimName: claim-storage-for-app

Kubernetes 的 Azure 文件

当您以ReadWriteOnce访问模式类型挂载 Azure 磁盘时,它将仅对 AKS 中的单个 pod 可用。因此,您需要使用 Azure 文件来在多个 pod 之间共享持久卷。Azure 文件的配置与 Azure 磁盘并没有太大不同,如前一节所述。创建存储类的 YAML 文件如下:

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: azurefile
provisioner: kubernetes.io/azure-file
mountOptions:
  - dir_mode=0888
  - file_mode=0888
  - uid=1000
  - gid=1000
  - mfsymlinks
  - nobrl
  - cache=none
parameters:
skuName: Standard_LRS

通过执行以下 YAML 文件使用持久卷索赔来提供 Azure 文件共享:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: azurefile
spec:
accessModes:
    - ReadWriteMany
storageClassName: azurefile
  resources:
    requests:
      storage: 5Gi

按以下方式应用这两个 YAML 文件:

使用持久卷索赔创建 Azure 文件

图 10.52:使用持久卷索赔创建 Azure 文件

执行 Azure 文件存储创建 YAML 和存储卷索赔 YAML 的结果如下:

验证 Azure 文件和 Azure 磁盘的创建

图 10.53:验证 Azure 文件和 Azure 磁盘的创建

如您所见,pod 中的规范保持不变。通过这些逐步实施,我们成功地为持久存储需求创建了 Azure 磁盘和 Azure 文件。

总结

本章主要讨论了 Kubernetes。我们从描述开发人员可能的工作环境开始,即具有工具启动本地开发的良好工作站,即使在本地安装了 Kubernetes。我们以 Ubuntu Desktop 为例,但实际上,只要您对开发环境满意,就没有关系。

一切就绪后,我们使用 Azure CLI 和 PowerShell 覆盖了在 Azure 中配置 Kubernetes 集群。

在 Azure 中部署工作负载可以简单地执行kubectl run,但也探讨了更复杂的场景,如多容器应用程序。

作为开发人员,有两种工具可帮助简化您的开发流程:Draft 和 Helm。Draft 用于初始开发阶段,Helm 用于安装和维护应用程序之后。

Kubernetes 是一个管理容器的工具,使得部署、维护和更新工作负载变得容易。可伸缩性是使用 Kubernetes 的优势之一;甚至可以根据所需的 CPU 和内存资源自动扩展。

本章的最后一节涵盖了在 Kubernetes 中使用持久存储,实际上为您提供了比在容器中存储数据或直接将存储附加到容器中更好的方式。

在下一章中,我们将回到 DevOps 的 Ops 部分——即故障排除和监视您的工作负载,而工作负载指的是安装了 Linux 的虚拟机、容器和 AKS。

问题

  1. 什么是 Pod?

  2. 创建多容器 Pod 的一个好理由是什么?

  3. 您可以使用哪些方法在 Kubernetes 中部署应用程序?

  4. 您可以使用哪些方法来更新 Kubernetes 中的应用程序?

  5. 如果您想要升级控制平面,是否需要在 Kubernetes 中创建额外的节点?

  6. 您能想到为什么要使用 iSCSI 解决方案的原因吗?

  7. 作为练习,重新创建使用持久存储的多容器 Pod。

进一步阅读

本章的目标是提供一个实用的方法,让您的工作负载在 Azure 云中运行。我们希望这是您进入 Kubernetes 世界的开始。还有很多东西等待您去发现!

Nigel Poulton 是一位已经写过关于 Docker 的优秀书籍的作者,他还写了一本关于 Kubernetes 的书籍The Kubernetes Book。如果您对 Kubernetes 真的很新,这是一个很好的起点。Gigi Sayfan 写了Mastering Kubernetes。确保购买第二版!不仅因为第一版不太好,而且因为它是必备的,并提供比第一版更多的信息。

作为开发人员,您应该尝试Kubernetes for Developers:Joseph Heck 可以告诉您更多关于使用 Kubernetes 的开发生命周期,使用 Node.js 和 Python 的示例。在他的书的最后一章中,他提到了 Helm 和 Brigade 等新兴项目。我们希望这将在以后的版本中更详细地探讨,甚至可能在另一本书中。

谈到 Brigade,brigade.sh在其官方网站上被描述为“在云中运行可编写脚本的自动化任务的工具——作为您的 Kubernetes 集群的一部分。”这远远超出了本书的范围,而且它基本上还处于早期开发阶段。作为开发人员,您应该花一些时间阅读更多关于它并尝试它。

最后但同样重要的是,另一个值得一提的重要资源是 Azure 的开放服务经纪人(OSBA:osba.sh)。它没有出现在本章中,因为在撰写本文时它还不完全具备生产能力。OSBA 是与外部服务(如数据库和存储)通信的开放标准。这是另一种为容器提供数据和存储数据的解决方案。

第十一章:故障排除和监视工作负载

故障排除和日志记录非常相关;当您遇到问题时,您开始分析事件、服务和系统日志。

在云环境中解决问题和修复问题可能与在更经典的部署中进行故障排除不同。本章解释了在 Azure 环境中故障排除 Linux 工作负载的差异、挑战和新可能性。

在本章结束时,您将能够:

  • 使用不同工具在 Linux 系统中进行性能分析。

  • 监视 CPU、内存、存储和网络等指标的详细信息。

  • 使用 Azure 工具识别和解决问题。

  • 使用 Linux 工具识别和解决问题。

技术要求

对于本章,您需要运行 Linux 发行版的一个或两个 VM。如果您愿意,可以使用最小的大小。必须安装audit守护程序,并且为了具有要分析和理解的审计系统日志,最好安装 Apache 和 MySQL/MariaDB 服务器。

以下是 CentOS 的一个示例:

sudo yum groups install ''Basic Web Server''
sudo yum install mariadbmariadb-server
sudo yum install setroubleshoot
sudosystemctl enable --now apache2
sudosystemctl enable --now mariadb

auditd通过使用可以根据您的需求进行修改的审计规则,提供关于服务器性能和活动的深入详细信息。要安装audit守护程序,请使用以下命令:

sudo yum list audit audit-libs

执行上述命令后,您将获得以下输出:

安装审计守护程序

图 11.1:安装审计守护程序

如果您可以看到如前所示的已安装的审计软件包列表,则已经安装了;如果没有,则运行以下命令:

sudo yum install audit audit-libs

成功安装auditd后,您需要启动auditd服务以开始收集审计日志,然后存储日志:

sudo systemctl start auditd

如果您想在启动时启动auditd,那么您必须使用以下命令:

sudo systemctl enable auditd

现在让我们验证auditd是否已成功安装并开始使用以下命令收集日志:

tail -f /var/log/audit/audit.log

验证 auditd 的安装和日志收集

图 11.2:验证成功安装 auditd 并收集日志

在本章中,我们将涵盖一般的 Azure 管理和 Azure Monitor。Linux 的 Log Analytics 代理,用于从 VM 收集信息,不受每个 Linux 发行版的支持;在决定要在本章中使用哪个发行版之前,请访问docs.microsoft.com/en-us/azure/virtual-machines/extensions/oms-linux

注意

运营管理套件OMS)通常已停用,并过渡到 Azure,名称“OMS”不再在任何地方使用,除了一些变量名称。现在它被称为 Azure Monitor。有关命名和术语更改的更多信息,请参阅docs.microsoft.com/en-gb/azure/azure-monitor/terminology,或者您也可以在docs.microsoft.com/en-us/azure/azure-monitor/platform/oms-portal-transition获取有关过渡的详细信息。

访问您的系统

学会排除工作负载将有助于您的日常工作。在 Azure 中进行故障排除与在其他环境中进行故障排除并无不同。在本节中,我们将看到一些技巧和窍门,这些将有助于您的日常工作。

无远程访问

当您无法通过 SSH 访问 Azure VM 时,可以通过 Azure 门户运行命令。

要在 Azure 门户上的 Azure VM 上运行命令,请登录到 Azure 门户,导航到您的 VM 并选择运行命令

导航到 Azure 门户中的 VM 部分的命令列表

图 11.3:导航到 Azure 门户中的 VM 部分

或者,您可以使用命令行,如下所示:

az vm run-command invoke --name <vm name> \
  --command-id RunShellScript \
  --scripts hostnamectl \
  --resource-group <resource group>

az vm run命令可用于在 VM 中运行 shell 脚本,用于一般机器或应用程序管理以及诊断问题。

无论是通过命令行还是通过 Azure 门户,只有在 Microsoft Azure Linux 代理仍在运行并可访问时,az vm命令才能正常工作。

注意

您可以在github.com/Azure/azure-powershell获取最新的 Microsoft Azure PowerShell 存储库,其中包含安装步骤及其用法。az正在取代 AzureRM,所有新的 Azure PowerShell 功能将只在az中提供。

根据安全最佳实践,您需要通过登录到 Azure 帐户并使用az vm user来重置密码来更改密码如下:

az vm user update \
  --resource-group myResourceGroup \
  --name myVM \
  --username linuxstar \
  --password myP@88w@rd

只有当您配置了具有密码的用户时才有效。如果您使用 SSH 密钥部署了 VM,那么您很幸运:同一部分中的重置密码选项将完成工作。

此选项使用 VMAccess 扩展(github.com/Azure/azure-linux-extensions/tree/master/VMAccess)。与前面讨论的运行命令选项一样,它需要 Azure VM 代理。

处理端口

您无法远程访问的原因可能与网络有关。在第五章高级 Linux 管理中,ip命令在网络部分中被简要介绍。您可以使用此命令验证 IP 地址和路由表。

在 Azure 站点上,必须检查网络和网络安全组,如第三章基本 Linux 管理中所述。在 VM 中,您可以使用ss命令,例如ip,它是iproute2软件包的一部分,用于列出处于监听状态的 UPD(-u)和 TCP(p)端口,以及打开端口的进程 ID(-p):

使用 ss -tulpn 命令检查端口详细信息

图 11.4:使用 ss -tulpn 命令检查端口

可以使用firewall-cmd --list-all --zone=public快速检查防火墙规则;如果有多个区域和接口,您需要为每个区域执行此操作。要包括 Azure Service Fabric 创建的规则,可以使用iptables-save

使用 iptables-save 命令包括 Azure Service Fabric 创建的规则

图 11.5:包括 Azure Service Fabric 创建的规则

不幸的是,在systemd单元级别没有可用的注释来查看所有访问规则的配置。不要忘记验证它们,如第六章管理 Linux 安全和身份中所讨论的那样。

使用 nftables

nftablesiptables更容易使用,并且它将整个iptables框架与简单的语法结合在一起。nftables建立在一个内核netfilter子系统上,可用于创建分组的复杂过滤规则。nftables 比iptables具有许多优点。例如,它允许您使用单个规则执行多个操作。它使用nft命令行工具,也可以使用nft -i命令以交互模式使用:

  1. 使用以下命令安装nftables
sudo apt install nftables
  1. 然后安装compat,加载与nftables内核子系统的兼容性:
apt install iptables-nftables-compat
  1. 最后,使用以下命令启用nftables服务:
sudo systemctl enable nftables.service
  1. 您可以使用以下命令查看当前的nft配置:
nft list ruleset
  1. 此外,您可以使用以下命令登录到nft交互模式:
nft –i
  1. 现在,您可以使用以下list命令列出现有的规则集:
nft> list ruleset
  1. 让我们创建一个新表,rule_table1
nft>add table inet rule_table1
  1. 现在,我们需要添加接受入站/出站流量的链命令如下:
nft>add chain inet rule_table1 input { type filter hook input priority 0 ; policy accept; }
nft>add chain inet rule_table1 output { type filter hook input priority 0 ; policy accept; }
  1. 您可以使用以下命令添加规则以接受 TCP(传输控制协议)端口:
nft>add rule inet rule_table1 input tcpdport { ssh, telnet, https, http } accept
nft>add rule inet rule_table1 output tcpdport { https, http } accept
  1. 这是我们新的nftables配置的输出:
nft> list ruleset
table inet rule_table1 {
        chain input {
                type filter hook input priority 0; policy accept;
tcpdport { ssh, telnet, http, https } accept
        }
        chain output {
                type filter hook input priority 0; policy accept;
tcpdport { http, https } accept
        }
}

引导诊断

假设您已经创建了您的 VM,可能是经过编排的,并且很可能是您自己的 VM,但它无法启动。

在启用 VM 的引导诊断之前,您需要一个存储账户来存储数据。您可以使用 az storage account list 列出已经可用的存储账户,如果需要,可以使用 az storage account create 命令创建一个存储账户。

现在让我们通过在 Azure CLI 中输入以下命令来启用引导诊断:

az vm boot-diagnostics enable --name <vm name>\
  --resource-group <resource group> \
  --storage <url>

区别在于您不需要存储账户的名称,而是需要存储 blob 的名称,可以通过 az storage account list 命令作为存储账户的属性找到。

在 Azure CLI 中执行以下命令以接收引导日志:

az vm boot-diagnostics get-boot-log \
--name <virtual machine> \
  --resource-group <resource group>

输出也会自动存储在一个文件中;在 Azure CLI 中,最好通过 less 进行管道处理或将其重定向到文件中。

Linux 日志

典型的 Linux 系统上运行着许多进程、服务和应用程序,它们产生不同的日志,如应用程序、事件、服务和系统日志,可用于审计和故障排除。在早期的章节中,我们遇到了 journalctl 命令,用于查询和显示日志。在本章中,我们将更详细地讨论这个命令,并看看如何使用 journalctl 实用程序来切割和处理日志。

在使用 systemd 作为其 init 系统的 Linux 发行版中,如 RHEL/CentOS、Debian、Ubuntu 和 SUSE 的最新版本中,使用 systemd-journald 守护程序进行日志记录。该守护程序收集单元的标准输出、syslog 消息,并且(如果应用程序支持)将应用程序的消息传递给 systemd。

日志被收集在一个数据库中,可以使用 journalctl 进行查询。

使用 journalctl

如果您执行 systemctl status <unit>,您可以看到日志的最后条目。要查看完整的日志,journalctl 是您需要的工具。与 systemctl 不同的是:您可以使用 -H 参数在其他主机上查看状态。您不能使用 journalctl 连接到其他主机。这两个实用程序都有 –M 参数,用于连接到 systemd-nspawnRkt 容器。

要查看日志数据库中的条目,请执行以下操作:

Sudo journalctl --unit <unit>

使用 journalctl --unit  命令查看日志数据库中的条目

图 11.6:查看日志数据库中的条目

默认情况下,日志使用 less 进行分页。如果您想要另一个分页器,比如 more,那么您可以通过 /etc/environment 文件进行配置。添加以下行:

SYSTEMD_PAGER=/usr/bin/more

以下是输出的示例:

使用 journalctl 命令获取进程的日志条目

图 11.7:使用 journalctl 命令获取进程的日志条目

让我们来看一下输出:

  • 第一列是时间戳。在数据库中,它是以 EPOCH 时间定义的,因此如果您更改时区,没有问题:它会被转换。

  • 第二列是主机名,由 hostnamectl 命令显示。

  • 第三列包含标识符和进程 ID。

  • 第四列是消息。

您可以添加以下参数来过滤日志:

  • --dmesg:内核消息,替代了旧的 dmesg 命令

  • --identifier:标识符字符串

  • --boot:当前引导过程中的消息;如果数据库在重启后是持久的,还可以选择以前的引导

过滤器

当然,您可以在标准输出上使用 grep,但 journalctl 有一些参数,可以帮助您过滤出所需的信息:

  • --priority:按 alertcritdebugemergerrinfonoticewarning 进行过滤。这些优先级的分类与 syslog 协议规范中的相同。

  • --since--until:按时间戳过滤。参考 man systemd.time 查看所有可能性。

  • --lines:行数,类似于 tail

  • --follow:类似于 tail -f 的行为。

  • --reverse:将最后一行放在第一行。

  • --output:将输出格式更改为 JSON 等格式,或者向输出添加更多详细信息。

  • --catalog:如果有消息的解释,则添加消息的解释。

所有过滤器都可以组合,就像这里一样:

sudo journalctl -u sshd --since yesterday --until 10:00 \
  --priority err

使用 journalctl 使用多个参数过滤日志条目

图 11.8:使用 journalctl 使用多个过滤器过滤日志条目

基于字段的过滤

我们还可以根据字段进行过滤。键入此命令:

sudojournactl _

现在按两次Ctrl + I;您将看到所有可用字段。这些过滤器也适用于相同的原则;也就是说,您可以将它们组合起来:

sudo journalctl _UID=1000 _PID=1850

您甚至可以将它们与普通过滤器组合使用:

sudo journalctl _KERNEL_DEVICE=+scsi:5:0:0:0 -o verbose

数据库持久性

现在,您可能需要出于合规原因或审计要求存储日志一段时间。因此,您可以使用 Azure Log Analytics 代理从不同来源收集日志。默认情况下,日志数据库不是持久的。为了使其持久化,出于审计或合规原因(尽管最佳做法不是将日志存储在本地主机),您必须编辑配置文件/etc/systemd/journald.conf

#Storage=auto行更改为此:

Storage=persistent

使用force重新启动systemd-journald守护程序:

sudo systemctl force-reload systemd-journald

使用此查看记录的引导:

sudo journalctl --list-boots

使用--list-boots 命令查看记录的引导

图 11.9:查看记录的引导

您可以使用--boot参数将引导 ID 作为过滤器添加:

journalctl --priority err --boot <boot id>

通过这种方式,hostnamectl的输出显示当前的引导 ID。

日志数据库不依赖于守护程序。您可以使用--directory--file参数查看它。

Syslog 协议

在实施 syslog 协议期间,Linux 和 Unix 家族的其他成员启用了日志记录。它仍然用于将日志发送到远程服务。

重要的是要理解该协议使用设施和严重性。这两者在 RFC 5424(tools.ietf.org/html/rfc5424)中标准化。在这里,设施指定记录消息的程序类型;例如,内核或 cron。严重性标签用于描述影响,例如信息或关键。

程序员的 syslog 手册(journald能够获取有关程序输出的所有内容。

添加日志条目

您可以手动向日志添加条目。对于 syslog,logger命令可用:

logger -p <facility.severity> "Message"

对于journald,有systemd-cat

systemd-cat --identifier <identifier> --priority <severity><command>

让我们看一个例子:

systemd-cat --identifier CHANGE --priority info \
  echo "Start Configuration Change"

作为标识符,您可以使用自由字符串或 syslog 设施。loggersystemd-cat都可以用于在日志中生成条目。如果应用程序不支持 syslog,则可以使用此功能;例如,在 Apache 配置中,您可以使用此指令:

errorlog  "tee -a /var/log/www/error/log  | logger -p local6.info"

您还可以将其作为变更管理的一部分使用。

将 journald 与 RSYSLOG 集成

为了为您自己的监控服务收集数据,您的监控服务需要 syslog 支持。这些监控服务的良好示例作为 Azure 中的现成 VM 提供:SplunkElastic Stack

RSYSLOG 是当今最常用的 syslog 协议实现。它已经默认安装在 Ubuntu、SUSE 和 Red Hat 等发行版中。

RSYSLOG 可以使用imjournal模块与日志数据库很好地配合。在 SUSE 和 Red Hat 等发行版中,这已经配置好;在 Ubuntu 中,您必须对/etc/rsyslog.conf文件进行修改:

# module(load="imuxsock")
module(load="imjournal")

修改后,重新启动 RSYSLOG:

sudo systemctl restart rsyslog

使用/etc/rsyslog.d/50-default.conf中的设置,它记录到纯文本文件中。

要将来自本地 syslog 的所有内容发送到远程 syslog 服务器,您必须将以下内容添加到此文件中:

*. *  @<remote server>:514

注意

这是 Ubuntu 中的文件名。在其他发行版中,请使用/etc/rsyslog.conf

如果要使用 TCP 协议而不是 UDP 协议,请使用@@

其他日志文件

您可以在/var/log目录结构中找到不支持 syslog 或systemd-journald的应用程序的日志文件。要注意的一个重要文件是/var/log/waagent.log文件,其中包含来自 Azure Linux VM 代理的日志记录。还有/var/log/azure目录,其中包含来自其他 Azure 代理(如 Azure Monitor)和 VM 扩展的日志记录。

Azure Log Analytics

Azure Log Analytics 是 Azure Monitor 的一部分,它收集和分析日志数据并采取适当的操作。它是 Azure 中的一个服务,可以从多个系统中收集日志数据并将其存储在一个中心位置的单个数据存储中。它由两个重要组件组成:

  • Azure Log Analytics 门户,具有警报、报告和分析功能

  • 需要在 VM 上安装的 Azure Monitor 代理

还有一个移动应用程序(在 iOS 和 Android 商店中,您可以在Microsoft Azure下找到它),如果您想在外出时查看工作负载的状态。

配置 Log Analytics 服务

在 Azure 门户中,从左侧栏选择所有服务,并搜索Log Analytics。选择添加并创建新的 Log Analytics 工作区。在撰写本文时,它并不是在所有地区都可用。使用该服务并不受地区限制;如果 VM 位于另一个地区,您仍然可以监视它。

注意

此服务没有预付费用,您按使用量付费!阅读aka.ms/PricingTierWarning获取更多详细信息。

另一种创建服务的方法是使用 Azure CLI:

az extension add -n application-insights

创建服务后,会弹出一个窗口,允许您导航到新创建的资源。或者,您可以在所有服务中再次搜索。

请注意,在资源窗格的右上角,Azure Monitor 和工作区 ID;您以后会需要这些信息。转到高级设置以找到工作区密钥。

在 Azure CLI 中,您可以使用以下命令收集此信息:

az monitor app-insights component create --app myapp
   --location westus1
   --resource-group my-resource-grp

要列出 Azure 订阅的所有工作区,可以使用以下 Azure CLI 命令:

az ml workspace list

您可以使用以下 Azure CLI 命令以 JSON 格式获取有关工作区的详细信息:

az ml workspace show -w my-workspace -g my-resource-grp

安装 Azure Log Analytics 代理

在安装 Azure Monitor 代理之前,请确保已安装audit包(在auditd中)。

要在 Linux VM 中安装 Azure Monitor 代理,您有两种可能性:启用 VM 扩展OMSAgentforLinux,或在 Linux 中下载并安装 Log Analytics 代理。

首先,设置一些变量以使脚本编写更容易:

$rg = "<resource group>"
$loc = "<vm location>"
$omsName = "<OMS Name>"
$vm = "<vm name">

您需要工作区 ID 和密钥。Set-AzureVMExtension cmdlet 需要以 JSON 格式的密钥,因此需要进行转换:

$omsID = $(Get-AzOperationalInsightsWorkspace '
 -ResourceGroupName $rg -Name $omsName.CustomerId) 
$omsKey = $(Get-AzOperationalInsightsWorkspaceSharedKeys '
 -ResourceGroupName $rg -Name $omsName).PrimarySharedKey
$PublicSettings = New-Object psobject | Add-Member '
 -PassThruNotePropertyworkspaceId $omsId | ConvertTo-Json
$PrivateSettings = New-Object psobject | Add-Member '
 -PassThruNotePropertyworkspaceKey $omsKey | ConvertTo-Json

现在您可以将扩展添加到虚拟机:

Set-AzureVMExtension -ExtensionName "OMS" '
  -ResourceGroupName $rg -VMName $vm '
  -Publisher "Microsoft.EnterpriseCloud.Monitoring" 
  -ExtensionType "OmsAgentForLinux" -TypeHandlerVersion 1.0 '
  -SettingString $PublicSettings
  -ProtectedSettingString $PrivateSettings -Location $loc

上述过程相当复杂并且需要一段时间。下载方法更简单,但您必须以访客身份通过 SSH 登录到 VM。当然,这两种方法都可以自动化/编排:

cd /tmp
wget \
https://github.com/microsoft/OMS-Agent-for-Linux \
/raw/master/installer/scripts/onboard_agent.sh
sudo -s 
sh onboard_agent.sh -w <OMS id> -s <OMS key> -d \
  opinsights.azure.com

如果在安装代理时遇到问题,请查看/var/log/waagent.log/var/log/azure/Microsoft.EnterpriseCloud.Monitoring.OmsAgentForLinux/*/extension.log配置文件。

扩展的安装还会创建一个配置文件rsyslog,/etc/rsyslogd.d/95-omsagent.conf

kern.warning @127.0.0.1:25224
user.warning @127.0.0.1:25224
daemon.warning @127.0.0.1:25224
auth.warning @127.0.0.1:25224
syslog.warning @127.0.0.1:25224
uucp.warning @127.0.0.1:25224
authpriv.warning @127.0.0.1:25224
ftp.warning @127.0.0.1:25224
cron.warning @127.0.0.1:25224
local0.warning @127.0.0.1:25224
local1.warning @127.0.0.1:25224
local2.warning @127.0.0.1:25224
local3.warning @127.0.0.1:25224
local4.warning @127.0.0.1:25224
local5.warning @127.0.0.1:25224
local6.warning @127.0.0.1:25224
local7.warning @127.0.0.1:25224

基本上意味着 syslog 消息(facility.priority)被发送到 Azure Monitor 代理。

在新资源的底部窗格中,有一个名为开始使用 Log Analytics的部分:

在 Azure 门户中开始使用 Log Analytics 部分

图 11.10:在 Azure 门户中开始使用 Log Analytics 部分

单击Azure 虚拟机(VMs)。您将看到此工作区中可用的虚拟机:

工作区中可用的虚拟机

图 11.11:工作区中可用的虚拟机

上述屏幕截图表示工作区中可用的虚拟机。它还显示我们已连接到数据源。

获取数据

在此资源的高级设置部分,您可以添加性能和 syslog 数据源。您可以使用特殊的查询语言通过日志搜索访问所有数据。如果您对这种语言不熟悉,您应该访问docs.loganalytics.io/docs/Learn/Getting-Started/Getting-started-with-queriesdocs.loganalytics.io/index

现在,只需执行此查询:

search *

为了查看是否有可用的数据,将搜索限制为一个 VM:

search * | where Computer == "centos01"

或者,为了获取所有的 syslog 消息,作为一个测试,您可以重新启动您的 VM,或者尝试这个:

logger -t <facility>. <priority> "message"

在 syslog 中执行以下查询以查看结果:

Syslog | sort 

如果您点击保存的搜索按钮,还有许多示例可用。

监控解决方案提供了一个非常有趣的附加功能,使这个过程变得更加容易。在资源窗格中,点击查看解决方案

导航到 VM 中的查看解决方案选项

图 11.12:导航到监控解决方案选项

选择所需的选项,然后点击添加

日志分析中的管理解决方案

图 11.13:日志分析中的管理解决方案

服务地图是一个重要的服务。它为您的资源提供了很好的概述,并提供了一个易于使用的界面,用于日志、性能计数器等。安装服务地图后,您必须在 Linux 机器上安装代理,或者您可以登录到门户并导航到 VM,它将自动为您安装代理:

cd /tmp
wget --content-disposition https://aka.ms/dependencyagentlinux \
-O InstallDependencyAgent-Linux64.bin
sudo sh InstallDependencyAgent-Linux64.bin -s

安装后,选择虚拟机 > 监视 > 洞察 > 服务地图

现在,点击摘要

服务地图中的摘要部分

图 11.14:服务地图摘要部分

您可以监视您的应用程序,查看日志文件等:

检查日志文件以监视应用程序

图 11.15:服务地图概述

日志分析和 Kubernetes

为了管理您的容器,您需要对 CPU、内存、存储和网络使用情况以及性能信息有详细的了解。Azure 监视可以用于查看 Kubernetes 日志、事件和指标,允许从一个位置监视容器。您可以使用 Azure CLI、Azure PowerShell、Azure 门户或 Terraform 为您的新或现有的 AKS 部署启用 Azure 监视。

创建一个新的az aks create命令:

az aks create --resource-group MyKubernetes --name myAKS --node-count 1 --enable-addons monitoring --generate-ssh-keys

要为现有的 AKS 集群启用 Azure 监视,使用az aks命令进行修改:

az aks enable-addons -a monitoring -n myAKS -g MyKubernetes

您可以从 Azure 门户为您的 AKS 集群启用监视,选择监视,然后选择容器。在这里,选择未监视的集群,然后选择容器,点击启用

从 Azure 门户监视 AKS 集群

图 11.16:从 Azure 门户监视 AKS 集群

您网络的日志分析

Azure Log Analytics 中的另一个解决方案是 Traffic Analytics。它可视化工作负载的网络流量,包括开放端口。它能够为安全威胁生成警报,例如,如果应用程序尝试访问其不允许访问的网络。此外,它提供了具有日志导出选项的详细监控选项。

如果您想使用 Traffic Analytics,首先您必须为您想要分析的每个区域创建一个网络监视器:

New-AzNetworkWatcher -Name <name> '
 -ResourceGroupName<resource group> -Location <location>

之后,您必须重新注册网络提供程序并添加 Microsoft Insights,以便网络监视器可以连接到它:

Register-AzResourceProvider -ProviderNamespace '
 "Microsoft.Network"
Register-AzResourceProvider -ProviderNamespaceMicrosoft.Insights

不能使用这个解决方案与其他提供商,如Microsoft.ClassicNetwork

下一步涉及使用“网络安全组(NSG)”,它通过允许或拒绝传入流量来控制日志流量的流动。在撰写本文时,这只能在 Azure 门户中实现。在 Azure 门户的左侧栏中,选择“监视”>“网络观察程序”,然后选择“NSG 流日志”。现在你可以选择要为其启用“NSG 流日志”的 NSG。

启用它,选择一个存储账户,并选择你的 Log Analytics 工作空间。

信息收集需要一些时间。大约 30 分钟后,第一批信息应该可见。在 Azure 门户的左侧栏中选择“监视”,转到“网络观察程序”,然后选择“Traffic Analytics”。或者,从你的 Log Analytics 工作空间开始:

从 Azure 门户检查 Traffic Analytics 选项,查看网络流量分布

图 11.17:使用 Traffic Analytics 查看网络流量分布

性能监控

在 Azure Monitor 中,有许多可用于监控的选项。例如,性能计数器可以让你深入了解你的工作负载。还有一些特定于应用程序的选项。

即使你不使用 Azure Monitor,Azure 也可以为每个 VM 提供各种指标,但不在一个中心位置。只需导航到你的 VM。在“概述”窗格中,你可以查看 CPU、内存和存储的性能数据。详细信息可在“监视”下的“指标”部分中找到。各种数据都可以获得,如 CPU、存储和网络数据:

使用 Azure 门户的概述窗格查看 VM 的性能数据

图 11.18:查看 VM 的性能数据

许多解决方案的问题在于它们是特定于应用程序的,或者你只是看到了最终结果,却不知道原因是什么。如果你需要了解虚拟机所使用资源的一般性能信息,可以使用 Azure 提供的信息。如果你需要了解你正在运行的 Web 服务器或数据库的信息,可以看看是否有 Azure 解决方案。但在许多情况下,如果你也能在 VM 中进行性能故障排除,那将非常有帮助。在某种程度上,我们将从第三章《基本 Linux 管理》中的“进程管理”部分开始。

在我们开始之前,有多种方法和方式可以进行性能故障排除。这本书能提供你唯一应该使用的方法,或者告诉你唯一需要的工具吗?不,不幸的是!但它可以让你意识到可用的工具,并至少涵盖它们的基本用法。对于更具体的需求,你总是可以深入研究 man 页面。在这一部分,我们特别关注负载是什么,以及是什么导致了负载。

最后一点:这一部分被称为“性能监控”,但这可能不是完美的标题。它是平衡监控、故障排除和分析。然而,在每个系统工程师的日常生活中,这种情况经常发生,不是吗?

并非所有提到的工具都默认在 Red Hat/CentOS 存储库中可用。你需要配置epel存储库:yum install epel-release

使用 top 显示 Linux 进程

如果你研究性能监控和 Linux 等主题,top总是被提到。它是用来快速了解系统上正在运行的内容的头号命令。

你可以用top显示很多东西,它带有一个很好的 man 页面,解释了所有的选项。让我们从屏幕顶部开始关注最重要的部分:

使用 top 命令显示 Linux 进程

图 11.19:使用 top 命令显示资源使用情况

让我们看看前面截图中提到的选项:

  • wa): 如果此值持续超过 10%,这意味着底层存储正在减慢服务器。此参数显示 CPU 等待 I/O 进程的时间。Azure VM 使用 HDD 而不是 SSD,使用多个 HDD 在 RAID 配置中可能有所帮助,但最好迁移到 SSD。如果这还不够,也有高级 SSD 解决方案可用。

  • us): 应用程序的 CPU 利用率;请注意,CPU 利用率是跨所有 CPU 总计的。

  • sy): CPU 在内核任务上花费的时间。

  • Swap:由于应用程序没有足够的内存而导致的内存分页。大部分时间应该为零。

top 屏幕底部还有一些有趣的列:

从 top 命令获取的输出的底部条目

图 11.20:从 top 命令获取的输出的底部条目

就个人而言,我们不建议现在担心优先级和 nice 值。对性能的影响很小。第一个有趣的字段是 VIRT(虚拟内存)。这指的是程序目前可以访问的内存量。它包括与其他应用程序共享的内存、视频内存、应用程序读入内存的文件等。它还包括空闲内存、交换内存和常驻内存。常驻内存是此进程实际使用的内存。SHR 是应用程序之间共享的内存量。这些信息可以让您对系统上应配置多少swap有一个概念:取前五个进程,加上 VIRT,然后减去 RESSHR。这并不完美,但是是一个很好的指标。

在前面截图中的 S 列是机器的状态:

  • D 是不可中断的睡眠,大多数情况是由于等待存储或网络 I/O。

  • R 正在运行—消耗 CPU。

  • S 正在休眠—等待 I/O,没有 CPU 使用。等待用户或其他进程触发。

  • T 被作业控制信号停止,大多数情况是因为用户按下 Ctrl + Z

  • Z 是僵尸—父进程已经死亡。在内核忙于清理时,它被内核标记为僵尸。在物理机器上,这也可能是 CPU 故障的迹象(由温度或糟糕的 BIOS 引起);在这种情况下,您可能会看到许多僵尸。在 Azure 中,这不会发生。僵尸不会造成伤害,所以不要杀死它们;内核会处理它们。

top 替代方案

有许多类似于 top 的实用程序,例如 htop,它看起来更漂亮,更容易配置。

非常相似但更有趣的是 atop。它包含所有进程及其资源使用情况,甚至包括在 atop 屏幕更新之间死亡的进程。这种全面的记账对于理解个别短暂进程的问题非常有帮助。atop 还能够收集有关运行容器、网络和存储的信息。

另一个是 nmon,它类似于 atop,但更专注于统计数据,并提供更详细的信息,特别是内存和存储方面的信息:

使用 nmon 命令获取内存、CPU 和存储性能详细信息

图 11.21:内存、CPU 和存储性能详细信息

nmon 也可以用来收集数据:

nmon -f -s 60 -c 30

上述命令每分钟收集 30 轮信息,以逗号分隔的文件格式,易于在电子表格中解析。在 IBM 的开发者网站 nmon.sourceforge.net/pmwiki.php?n=Site.Nmon-Analyser 上,您可以找到一个 Excel 电子表格,使这变得非常容易。它甚至提供了一些额外的数据分析选项。

glances 最近也变得非常受欢迎。它基于 Python,并提供有关系统、正常运行时间、CPU、内存、交换、网络和存储(磁盘 I/O 和文件)的当前信息:

使用 glances 实用程序查看性能

图 11.22:使用 glances 实用程序查看性能

glancestop的最先进的替代品。它提供了所有替代品的功能,而且,您还可以远程使用它。您需要提供服务器的用户名和密码来启动glances

glances --username <username> --password <password> --server 

在客户端上也执行以下操作:

glances --client @<ip address>

默认情况下,使用端口61209。如果使用--webserver参数而不是--server,甚至不需要客户端。端口61208上提供完整的 Web 界面!

glances能够以多种格式导出日志,并且可以使用 API 进行查询。对SNMP(简单网络管理协议)协议的实验性支持也正在进行中。

Sysstat-一组性能监控工具

sysstat软件包包含性能监控实用程序。在 Azure 中最重要的是sariostatpidstat。如果还使用 Azure Files,cifsiostat也很方便。

sar是主要实用程序。主要语法是:

sar -<resource> interval count

例如,使用以下命令报告 CPU 统计信息 5 次,间隔为 1 秒:

sar -u 1 5

要监视核心12,请使用此命令:

sar -P 1 2 1 5

(如果要单独监视所有核心,可以使用ALL关键字。)

以下是一些其他重要资源:

  • -r:内存

  • -S:交换

  • -d:磁盘

  • -n <type>:网络类型,例如:

DEV:显示网络设备统计

EDEV:显示网络设备故障(错误)统计

NFS:显示SOCK:显示 IPv4 中正在使用的套接字

IP:显示 IPv4 网络流量

TCP:显示 TCPv4 网络流量

UDP:显示 UDPv4 网络流量

ALL:显示所有前述信息

pidstat可以通过其进程 ID 从特定进程收集 CPU 数据。在下一个截图中,您可以看到每 5 秒显示 2 个样本。pidstat也可以对内存和磁盘执行相同的操作:

使用 pidstat 命令获取特定进程的 CPU 数据性能

图 11.23:使用 pidstat 显示 CPU 统计信息

iostat是一个实用程序,顾名思义,它可以测量 I/O,但也可以创建 CPU 使用情况报告:

使用 iostat 命令获取 I/O 性能统计

图 11.24:使用 iostat 获取 CPU 和设备报告和统计信息

tps表示每秒向设备发出的传输次数。kb_read/skB_wrtn/s是在 1 秒内测得的千字节数;前面截图中的avg-cpu列是自 Linux 系统启动以来的统计总数。

在安装sysstat软件包时,在/etc/cron.d/sysstat文件中安装了一个 cron 作业。

注意

在现代 Linux 系统中,systemd-timers和使用cron的旧方法都可用。sysstat仍然使用cron。要检查cron是否可用并正在运行,请转到systemctl | grep cron

cron每 10 分钟运行一次sa1命令。它收集系统活动并将其存储在二进制数据库中。每天一次,执行sa2命令生成报告。数据存储在/var/log/sa目录中。您可以使用sadf查询该数据库:

使用 sadf 命令查询系统活动的数据库

图 11.25:使用 sadf 查询系统活动的数据库

此截图显示了 11 月 6 日09:00:0010:10:00之间的数据。默认情况下,它显示 CPU 统计信息,但您可以使用与sar相同的参数进行自定义:

sadf /var/log/sa/sa03 -- -n DEV

这显示了 11 月 6 日每个网络接口的网络统计信息。

dstat

sysstat用于历史报告,而dstat用于实时报告。虽然topps的监视版本,但dstatsar的监视版本:

使用 dstat 命令获取实时报告

图 11.26:使用 dstat 获取实时报告

如果您不想一次看到所有内容,可以使用以下参数:

  • c:CPU

  • d:磁盘

  • n:网络

  • g:分页

  • s:交换

  • m:内存

使用 iproute2 进行网络统计

在本章的前面,我们谈到了ip。这个命令还提供了一个选项,用于获取网络接口的统计信息:

ip -s link show dev eth0

使用 ip -s link show dev eth0 命令获取网络接口的统计信息

图 11.27:获取网络接口的统计信息

它解析来自/proc/net目录的信息。另一个可以解析此信息的实用程序是ss。可以使用以下命令请求简单摘要:

ss -s

使用-t参数不仅可以显示处于监听状态的端口,还可以显示特定接口上的传入和传出流量。

如果您需要更多详细信息,iproute2软件包提供了另一个实用程序:nstat。使用-d参数,甚至可以在间隔模式下运行它:

使用 nstat 实用程序获取处于监听状态的端口的详细报告

图 11.28:获取有关处于监听状态的端口的详细报告

这已经比ss的简单摘要要多得多。但是iproute2软件包还有更多提供:lnstat

这是提供网络统计信息的命令,如路由缓存统计:

lnstat––d

使用 lnstat-d 命令获取网络统计信息

图 11.29:使用 lnstat -d 获取网络统计信息

这显示了它可以显示或监视的所有内容。这相当低级,但我们使用lnstat -f/proc/net/stat/nf_conntrack解决了一些与防火墙性能相关的问题,同时监视drops计数器。

使用 IPTraf-NG 进行网络监控

您可以从nmon等工具获取网络详细信息,但如果您想要更多详细信息,那么 IPTraf-NG 是一个非常好的实时基于控制台的网络监控解决方案。它是一个基于控制台的网络监控实用程序,可以收集所有网络 IP、TCP、UDP 和 ICMP 数据,并能够根据 TCP/UDP 的大小来分解信息。还包括一些基本过滤器。

一切都在一个菜单驱动的界面中,所以没有必须记住的参数:

IPTraf-NG 的菜单窗口

图 11.30:IPTraf-NG 的菜单窗口

tcpdump

当然,tcpdump不是性能监控解决方案。这个实用程序是监视、捕获和分析网络流量的好工具。

要查看所有网络接口上的网络流量,请执行以下操作:

tcpdump -i any 

对于特定接口,请尝试这个:

tcpdump -i eth0 

一般来说,最好不要解析主机名:

tcpdump -n -i eth0

通过重复v参数,可以添加不同级别的详细程度,最大详细程度为三:

tcpdump -n -i eth0 -vvv

您可以基于主机筛选流量:

tcpdump host <ip address> -n -i eth0 

或者,您可以基于源或目标 IP 进行筛选:

tcpdump src <source ip address> -n -i eth0 
tcpdump dst <destination ip address> -n -i eth0

还可以根据特定端口进行筛选:

tcpdump port 22 
tcpdumpsrc port 22
tcpdump not port 22

所有参数都可以组合使用:

tcpdump -n dst net <subnet> and not port ssh -c 5

添加了-c参数,因此只捕获了五个数据包。您可以将捕获的数据保存到文件中:

tcpdump -v -x -XX -w /tmp/capture.log       

添加了两个参数,以增加与其他可以读取tcpdump格式的分析器的兼容性:

  • -XX:以十六进制和 ASCII 格式打印每个数据包的数据

  • -x:为每个数据包添加标题

要以人类可读的完整时间戳格式读取数据,请使用此命令:

tcpdump -tttt -r /tmp/capture.log

注意

另一个很棒的网络分析器是 Wireshark。这是一个图形工具,适用于许多操作系统。该分析器可以导入从tcpdump捕获的数据。它配备了一个很棒的搜索过滤器和分析工具,适用于许多不同的网络协议和服务。

在虚拟机中进行捕获并将其下载到工作站以便在 Wireshark 中进一步分析数据是有意义的。

我们相信您现在可以使用不同的工具在 Linux 系统中实现良好的性能分析,以监视 CPU、内存、存储和网络详细信息。

摘要

在本章中,我们涵盖了有关故障排除、日志记录、监控甚至分析的几个主题。从获取对虚拟机的访问开始,我们研究了在 Linux 中本地和远程进行日志记录。

性能监控和性能故障排除之间有一条细微的界限。有许多不同的实用工具可用于找出性能问题的原因。每个工具都有不同的目标,但也有很多重叠之处。我们已经介绍了 Linux 中最流行的实用工具以及一些可用的选项。

在第一章中,我们看到 Azure 是一个非常开放源代码友好的环境,微软已经付出了很大的努力,使 Azure 成为一个开放的、标准的云解决方案,并考虑了互操作性。在本章中,我们看到微软不仅在部署应用程序时支持 Linux,而且在 Azure Monitor 中也支持它。

问题

  1. 为什么在虚拟机中至少应该有一个带密码的用户?

  2. systemd-journald守护进程的目的是什么?

  3. syslog 设施是什么?

  4. syslog 中有哪些可用的优先级?

  5. 你如何向日志添加条目,以及为什么要这样做?

  6. 在 Azure 中有哪些服务可用于查看指标?

  7. 为什么top只能用于初步查看与性能相关的问题,以及哪个实用工具可以解决这个问题?

  8. sysstatdstat实用程序之间有什么区别?

  9. 为什么应该在工作站上安装 Wireshark?

进一步阅读

一个重要的信息来源是 Brendan D Gregg 的网站(www.brendangregg.com),他在那里分享了一份令人难以置信的长长的 Linux 性能文档、幻灯片、视频等清单。除此之外,还有一些不错的实用工具!他是 2015 年教会我的人,正确识别问题是很重要的。

  • 是什么让你觉得有问题?

  • 曾经有没有出现过问题?

  • 最近有什么变化吗?

  • 尝试寻找技术描述,比如延迟、运行时错误等。

  • 只有应用程序受影响,还是其他资源也受到影响?

  • 提出一个关于环境的确切描述。

你还需要考虑以下几点:

  • 是什么导致负载(哪个进程、IP 地址等)?

  • 为什么称之为负载?

  • 负载使用了哪些资源?

  • 负载是否发生变化?如果是,它是如何随时间变化的?

最后但同样重要的是,本书作者是 Benjamin Cane 的《Red Hat Enterprise Linux 故障排除指南》。我知道,这本书的一些部分已经过时,因为它是在 2015 年印刷的。当然,我希望有第二版,但是,特别是如果你是 Linux 的新手,买这本书。

第十二章:附录

本章提供了对前几章中提出的所有问题的一套解决方案。如果您已经回答了这些问题,可以检查您的答案的准确性。如果您当时无法找到解决方案,可以参考这里给出的相应章节的答案。

第一章:探索 Microsoft Azure 云

  1. 您可以虚拟化计算、网络和存储资源。当然,最终,您仍然需要硬件在世界的某个地方运行 hypervisors,可能还需要一个云平台。

  2. 虚拟化模拟硬件,容器模拟运行在底层操作系统上的多个容器的操作系统。在虚拟化中,每个虚拟机都有自己的内核;它们不使用 hypervisor/硬件内核。在硬件虚拟化中,一切都被转换为软件。在容器虚拟化中,只有进程被隔离。

  3. 这取决于;您是否在同一平台上开发应用程序?如果是这样,那么 PaaS 是适合您的服务类型;否则,请使用 IaaS。SaaS 提供了一个应用程序;它不是一个托管平台。

  4. 这取决于。Azure 符合并帮助您遵守法律规定和安全/隐私政策。此外,如果担心数据存储在世界其他地区,还有不同地区的概念。但总是有例外——大多数情况下是公司政策或政府裁决。

  5. 对于可伸缩性、性能和冗余性来说非常重要。

  6. 这是一个基于云的身份管理服务,用于控制对云和本地混合环境的访问。它允许您登录并访问云和本地环境,而不是使用自己的 AD 服务器并管理它们。

第二章:开始使用 Azure 云

  1. 它有助于自动化。除此之外,基于 Web 的门户经常更改,而命令行界面更加稳定。在我们看来,这也让您更好地了解底层技术,因为它的工作流程相对严格。

  2. 它提供了存储所有数据对象的访问权限。您需要一个用于引导诊断和 Azure Cloud Shell 数据的存储。更多细节可以在第四章 管理 Azure中找到。

  3. 在 Azure 中,存储帐户必须是全局唯一的。

  4. 报价是由发布者提供的一组相关图像,例如 Ubuntu 服务器。图像是一个特定的图像。

  5. 停止的 Azure 虚拟机会保留分配的资源,例如动态公共 IP 地址,并产生成本,而取消分配的虚拟机会释放所有资源,因此停止产生资源成本。但是,两者都会产生存储成本。

  6. 基于密钥的身份验证有助于自动化,因为它可以在不在脚本中暴露秘密/密码的情况下使用。

  7. 将创建公钥和私钥(如果仍然需要)并存储在您的主目录(~/.ssh)中;公钥将被添加到虚拟机中的authorized_keys文件中

第三章:基本 Linux 管理

  1. 对于用户 Lisa John Karel Carola; useradd $user; done

  2. 执行passwd <user>并输入welc0meITG,然后它会要求您再次输入密码以确认,因此再次输入welc0meITG

  3. getent<user>

  4. groupadd finance; groupadd staff

  5. groupmems -g <group_name> -a <user_name>;或者,usermod –a –G <group_name> <user_name>

  6. 要创建目录并设置组所有权,请执行以下操作:

mkdir /home/staff
chown staff /home/staff
chgrp staff /home/staff

同样,对于finance,执行以下命令:

mkdir /home/finance
chown finance /home/finance
chgrp finance /home/finance
  1. chmod –R g+r /home/finance

  2. 默认的获取访问控制列表(getfacl -d)将列出用户的 ACL。

第四章:管理 Azure

  1. 在使用 Azure 门户创建虚拟机时,您不需要任何东西。当您使用命令行时,您需要以下虚拟网络:

资源组

Azure 虚拟网络(VNet)

配置的子网

网络安全组

公共 IP 地址

网络接口

  1. 您需要诸如诊断和监控之类的名称服务,这些服务需要存储帐户。

  2. 有时(例如,对于存储帐户),名称必须是唯一的。将前缀与随机生成的数字结合在一起是使名称可识别和唯一的好方法。

  3. 定义可以在虚拟网络中使用的 IP 范围。

  4. 在虚拟网络中创建一个或多个子网,这些子网可以被隔离或路由到彼此,而不必离开虚拟网络。

  5. 网络安全组为网络提供 ACL,并为虚拟机或容器提供端口转发。

  6. 从虚拟机到互联网的流量通过源网络地址转换SNAT)发送。这意味着发出数据包的 IP 地址将被公共 IP 地址替换,这对于 TCP/IP 的出站和入站路由是必需的。

  7. 当虚拟机被停止时,动态分配的公共 IP 地址将被释放。当虚拟机再次启动时,它将获得另一个 IP 地址。当必须保持相同的 IP 地址即使服务 IP 地址发生变化时,您可以创建和分配静态公共 IP 地址。

第五章:高级 Linux 管理

  1. Linux 内核。

  2. systemd-udevd

  3. ls /sys/class/netip link show

  4. 用于 Linux 的 Azure 代理。

  5. ls /sys/class/netlsblklsscsi命令也可能有所帮助。

  6. 使用RAID0来提高性能并允许比仅使用单个磁盘更好的吞吐量是一个好主意。

  7. 在文件系统级别,使用mdadm逻辑卷管理器LVM)(本章未涉及)。

  8. 创建 RAID,格式化它,并创建一个挂载点:

mdadm --create /dev/md127 --level 0 --raid-devices 3 \    /dev/sd{c,d,e}mkfs.xfs -L myraid /dev/md127 mkdir /mnt/myraid

创建一个单元文件,/etc/systemd/system/mnt-myraid.mount

[Unit]Description = myRaid volume [Mount]Where = /mnt/myraid What = /dev/md127 Type = xfs [Install]WantedBy = local-fs.mount

在启动时启用它:

systemctl enable --now mnt-myraid.mount

第六章:管理 Linux 安全和身份

  1. 使用firewall-cmd文件或部署/etc/firewalld目录。

  2. --permanent参数使其在重新启动时持久,并在启动配置期间执行。

  3. 在 Linux 中,您可以使用 systemd 中的 ACL 来限制访问。一些应用程序还提供其他主机允许/拒绝选项。在 Azure 中,您可以使用网络安全组和 Azure 防火墙服务。

  4. 自主访问控制DAC)用于基于用户/组和文件权限限制访问。强制访问控制MAC)根据每个资源对象的分类标签进一步限制访问。

  5. 如果有人非法访问了一个应用程序或系统,使用 DAC,就没有办法阻止进一步的访问,特别是对于具有相同用户/组所有者和其他用户权限的文件。

  6. 每个设备都将有一个唯一的 MAC 地址,您可以使用ipconfig/ all找到您的虚拟机的 MAC 地址,然后查找物理地址。

利用 Linux 安全模块的 MAC 框架如下:

SELinux:基于 Red Hat 的发行版和 SUSE

AppArmor:Ubuntu 和 SUSE

较少人知道的 TOMOYO(SUSE):本书未涉及

  1. 除了 SELinux 可以保护更多的资源对象之外,AppArmor 直接与路径一起工作,而 SELinux 通过细粒度访问控制保护整个系统。

  2. 加入 AD 域之前,您需要以下先决条件:

用于授权的 Kerberos 客户端

realmadclinet命令

第七章:部署您的虚拟机

  1. 我们使用自动化部署来节省时间,快速建立可重现的环境,并避免手动错误。

  2. 除了上一个问题的答案,标准化的工作环境使基于团队的应用程序开发成为可能。

  3. 脚本非常灵活。脚本更容易创建,并且可以随时手动调用。自动化过程可以通过事件触发,例如使用git push向 Git 添加代码或停止/启动虚拟机。

  4. Azure 资源管理器是最重要的。此外,您可以使用 Terraform、Ansible 和 PowerShell。

  5. Vagrant 在 Azure 中部署工作负载;Packer 创建一个自定义镜像,您可以部署。

  6. 由于多种原因,最重要的原因如下:

安全性,使用 CIS 标准加固镜像

当需要对标准镜像进行定制时

不依赖于第三方的提供

捕获现有虚拟机

将快照转换为镜像

  1. 您可以通过构建自己的 VHD 文件来创建自己的镜像。以下是这样做的选项:

在 Hyper-V 或 VirtualBox 中创建一个虚拟机,这是 Windows、Linux 和 macOS 上可用的免费 hypervisor。

在 VMware Workstation 或 KVM 中创建虚拟机,并在 Linux qemu-img 中使用它来转换镜像。

第八章:探索持续配置自动化

示例脚本可在 GitHub 上找到github.com/PacktPublishing/Hands-On-Linux-Administration-on-Azure---Second-Edition/tree/master/chapter12/solutions_chapter08

第九章:Azure 中的容器虚拟化

  1. 您可以使用容器来打包和分发应用程序,这些应用程序可以是平台无关的。容器消除了虚拟机和操作系统管理的需求,并帮助您实现高可用性和可伸缩性。

  2. 如果您有一个需要底层虚拟机所有资源的庞大的单片应用程序,则容器不适用。

  3. Linux 容器LXCs)是可以在 Azure 中进行配置的最佳解决方案。

  4. 诸如 Buildah 之类的工具使得创建可用于各种解决方案的虚拟机成为可能。Rkt(发音为"rocket")也支持 Docker 格式。开放容器倡议正在努力创建标准,以使虚拟机的创建变得更加容易。

  5. 您可以在 Azure 中开发所有内容,也可以在本地开发,然后推送到远程环境。

  6. 它是容器平台无关的,Buildah 工具比其他工具更容易使用。您可以在github.com/containers/buildah上进一步探索。

  7. 容器可以根据需要构建、替换、停止和销毁,而不会对应用程序或数据产生任何影响,因此不建议在容器中存储任何数据。而是将其存储在卷中。

第十章:使用 Azure Kubernetes Service

  1. Pod 是一组具有共享资源(如存储和网络)的容器,以及如何运行容器的规范。

  2. 创建多容器 Pod 的一个很好的理由是为了支持主要应用程序的共同管理的辅助进程。

  3. 有多种可用的方法,包括 Draft 和 Helm,除了Azure Kubernetes ServiceAKS)还讨论了这些方法。

  4. 您可以使用kubectl来更新 AKS 中的应用程序。此外,您还可以使用 Helm 和 Draft。

  5. 您不需要手动执行此操作;AKS 将自动执行。

  6. 当您希望从多个容器同时读/写时,您将需要一个 iSCSI 解决方案和一个集群文件系统。

  7. 示例代码可在 GitHub 上找到github.com/MicrosoftDocs/azure-docs/raw/master/articles/aks/azure-disks-dynamic-pv.md

第十一章:故障排除和监视工作负载

  1. 您可以使用 Azure 串行控制台以 root 身份访问您的虚拟机,除非有特定的阻止。

  2. 为了收集所有标准输出、syslog 消息和来自内核、systemd 进程和单元的相关消息。

  3. syslog 使用以下严重性列表(每个应用程序):

警报:必须立即采取行动。

临界:临界条件。

错误:错误条件。

警告:警告条件。

注意:正常但重要的条件。

信息:信息消息。

调试:调试级别的消息。

  1. 0-紧急,1-警报,2-关键,3-错误,4-警告,5-通知,6-信息,7-调试。

  2. 使用loggersystemd-cat。如果应用程序或脚本不支持 syslog,则可以使用它。另一个选项是将日志条目作为变更管理的一部分添加。

  3. Azure Log Analytics 服务用于查看虚拟机的指标。

  4. top实用程序有几个缺点;例如,您无法看到短暂的进程。atopdstat实用程序是解决这个问题的解决方案。

  5. sysstat实用程序提供历史数据;dstat提供实时监控。

  6. 它使得来自 Azure 虚拟机(工作站)的tcpdump数据的收集更易于阅读,并具有很大的分析潜力。

posted @ 2024-05-16 19:06  绝不原创的飞龙  阅读(16)  评论(0编辑  收藏  举报