一、Traefik 简介
在过去的几年中,微服务已经成为企业应用开发的主流架构范例。它们已经取代了过去几十年来作为主流的应用开发的整体架构。单片应用是在模块化架构中开发的。这意味着被称为模块的离散逻辑组件被创建来基于它们的职责隔离组件。即使一个应用由离散的组件组成,它们也被打包并作为一个可执行文件部署。总的来说,应用具有非常紧密的耦合。对这些模块中的每一个的改变都不能单独发布。您需要每次发布一个完整的应用。
当您构建一个未知的应用时,单片架构非常适合。在这种情况下,您通常需要为每个功能快速构建原型。单片架构在这种情况下很有帮助,因为应用有一个统一的代码库。该架构具有以下优势。
开发简单。
测试简单。例如,您可以通过启动应用并使用 Selenium 测试 UI 来实现端到端测试。
易于部署。您只需将打包的应用复制到服务器上。
通过在负载平衡器后运行多个副本,可以轻松地进行水平扩展。
总之,您可以在这些早期阶段快速交付完整的应用。但是随着应用的有机增长,收益会逐渐减少。在后期阶段,应用变得更难维护和操作。大多数子组件承担更多的责任,成为大的子系统。每个子系统都需要一个开发团队来维护。因此,完整的应用通常由多个开发团队维护。但是应用具有高耦合性,因此开发团队在提供新功能时是相互依赖的。由于单一的二进制文件,该组织面临以下一系列问题。
季度发布:应用功能需要更多时间发布。大多数时候,一个应用的特性需要跨不同的子系统来处理。每个团队都可以进行开发,但是部署需要整套组件。因此,团队很少能独立工作。发布通常是跨不同团队的大规模协同工作,每个周期只能做几次。
弃用的技术:通常,当你使用技术时,你必须定期升级它。升级确保所有的漏洞都被覆盖。应用库通常需要频繁升级,因为它们还会添加新功能。但是升级一个整体的库是困难的。团队可以尝试使用最新的版本,但通常需要确保升级不会破坏其他子系统。在某些情况下,升级甚至会导致子系统的完全重写,这对业务来说是非常危险的。
陡峭的学习曲线:单片应用通常有很大的代码库。但是单个开发人员经常在代码库的一个非常小的子集上工作。乍一看,代码行给开发人员造成了心理瓶颈。此外,由于应用是紧密耦合的,开发人员通常需要知道其他人是如何调用代码的。因此,新开发人员的总体入职时间很长。即使是有经验的开发人员也很难对没有维护好的模块进行修改。这造成了一个随着时间推移而扩大的知识缺口。
应用扩展:通常情况下,单一应用只能垂直扩展。水平扩展应用是可能的,但是您需要确定每个子系统如何维护其内部状态。在任何情况下,应用都需要所有子系统的资源。资源不能有选择地提供给有负载的子系统。因此,对于整体式应用来说,这是一个要么全有要么全无的场景。这通常是一件昂贵的事情。
面对挑战时,组织会寻找替代架构来解决这些问题。
微服务架构
微服务架构是整体架构的替代方案(见图 1-1 )。它将单一应用转换为具有以下特征的分布式系统。
img/497627_1_En_1_Fig1_HTML.jpg
图 1-1
整体服务与微服务
服务:微服务是作为可以独立工作,提供一组业务能力的服务而开发的。服务可能依赖于其他服务来执行所需的功能。独立的团队可以开发这些服务。团队可以自由选择和升级他们服务所需的技术。组织通常将服务的全部责任委托给他们各自的团队。团队必须确保他们各自的服务按照约定的可用性运行,并满足约定的质量标准。
业务环境:服务通常是围绕业务领域创建的。这确保了它不会太细粒度或太大。服务需要首先回答它是所述业务功能的所有者还是该功能的消费者。函数所有者必须维护所有相应的函数数据。如果它需要更多的支持功能,它可能会从另一个服务中使用相同的功能。因此,确定业务上下文边界有助于检查服务依赖关系。微服务旨在构建一个具有松耦合和高内聚属性的系统。聚合所有逻辑上相关的功能使服务成为一个独立的产品。
应用治理:在企业系统中,治理扮演着重要的角色。你很少想制造难以运行的系统。由于这个原因,一个治理小组保持对开发人员使用的技术的检查,以便操作团队仍然可以运行系统。但是微服务架构为各个团队提供了完全的所有权。所有权不限于开发。它还委托服务操作。因此,大多数组织必须采用 DevOps 实践。这些实践使开发团队能够有效地操作和治理服务。
自动化:自动化在微服务中起着重要的作用。它适用于所有形式,如基础设施自动化、测试自动化和发布自动化。团队需要高效运作。他们需要更频繁地测试并快速发布。这只有在他们更多地依赖机器而不是人工干预的情况下才有可能。开发后的手工测试是一个主要的瓶颈。因此,团队经常以多种方式自动化他们的测试,比如 API 测试、冒烟测试、夜间测试等等。他们经常手动执行探索性测试来验证构建。发布和基础设施准备通常通过使用 DevOps 实践来自动化。
总之,一个整体有一个集中的经营模式。这意味着所有代码都驻留在一个地方;每个人都使用同一个库,同时发布,等等。但另一方面,微服务是一种完全去中心化的方法。团队可以完全自主地做出最佳决策。采用这样的架构不仅要求软件设计的改变,还要求组织交互的改变。组织从这种应用设计中获得了以下好处。
灵活
这是组织采用微服务架构的最大驱动因素之一。组织变得更具适应性,他们可以更快地响应不断变化的业务需求。该架构提供的松散耦合允许加速开发。在将小型、松散耦合的服务部署到生产环境之前,可以单独构建、修改和测试它们。该模型指示小型独立开发团队在其定义的边界内工作。这些团队负责维护高水平的软件质量和服务可用性。
创新ˌ革新
微服务架构促进了支持每个服务的独立小型开发团队。每个团队在其服务范围内拥有所有权。他们不仅负责开发,还负责运营服务。因此,团队采用了大量的自动化和工具来帮助他们实现这些目标。这些高层次的目标推动了组织内的工程文化。
此外,开发团队通常很清楚他们服务的缺点。这样的团队可以使用他们的自主决策能力来解决这些问题。他们可以经常解决问题并提高服务质量。在这里,团队被充分授权为他们的目的选择合适的工具和框架。它最终会提高整个产品的技术质量。
弹性
故障隔离是将故障的影响限制到有限的子系统/组件的行为。这个原则允许子系统失败,只要它不影响整个应用。微服务架构的分布式特性提供了故障隔离,这是构建弹性系统的主要要求。任何出现故障的服务都可以独立处理。开发人员可以修复问题并部署新版本,而应用的其余部分继续独立运行。
弹性,或者说容错,通常被定义为应用在某些部分出现故障的情况下正常运行的能力。像微服务这样的分布式系统是基于各种原则的,比如电路断开、节流来处理故障传播。这是一个重要的方面;如果做得好,它提供了一个弹性系统的好处。但是,如果这种情况不处理,就会由于故障级联而导致频繁停机。弹性还提高了业务敏捷性,因为开发人员可以发布新的服务,而不用担心系统中断。
可量测性
可伸缩性被定义为系统处理工作增长的能力。在一个整体中,很容易量化系统的可伸缩性。在单块系统中,随着负载的增加,并非所有子系统的流量都成比例增加。通常情况下,系统的某些部分比其他部分获得更多的流量。因此,整体系统性能由服务的子集决定。通过添加更多的硬件,可以更容易地扩展单芯片系统。但有时,这也很困难,因为不同的模块可能有冲突的资源需求。总的来说,一个杂草丛生的庞然大物没有充分利用硬件。它通常会降低系统性能。
微服务提供的分离使组织能够了解每个微服务所服务的流量。分而治之原则有助于提高整体系统性能。开发人员可以为每个服务采用适当的任务并行化或集群技术来提高系统吞吐量。他们可以采用适当的编程语言和框架,用尽可能好的配置进行微调。最后,可以通过研究服务需求来分配硬件,而不是扩展整个生态系统。
可维护性
技术债务是单体系统的一个主要问题。过度生长的巨石通常有不被整个团队很好理解的部分。在一个整体中解决技术债务是困难的,因为人们经常害怕破坏任何工作特性。曾经有过这样的例子,不需要的死代码通过解决某个特定模块上的技术债务而复活了。
微服务架构遵循分而治之的原则,有助于缓解这一问题。好处可以与面向对象的应用设计联系起来,在面向对象的应用设计中,系统被分解成对象。每个对象都有一个定义好的契约,从而改进整个系统的维护。开发人员可以对每个被重构的对象进行单元测试,以验证正确性。类似地,围绕业务环境创建的微服务有一个定义好的契约。这些松散耦合的服务可以单独重构和测试。开发人员可以在验证服务契约的同时解决服务的技术债务。采用微服务通常被称为一个整体的技术债务偿还。
您已经了解了微服务架构的优势。但是架构也带来了很多挑战。一些挑战是由于系统的分布式性质,而另一些挑战是由应用环境的多样性引起的。服务可以用不同的技术实现,并以不同的方式扩展。同一服务可以有多个版本来满足不同的需求。团队应该在应用设计期间制定策略来克服这些挑战,而不是事后才想到。应用部署就是这样一个重要方面。Monoliths 已经部署在一个三层模型上。但同样的模式在微服务上效果并不好。下一节讨论部署模型中所需的更改。
n-分层部署
层部署是一种设计实现,其中 web 应用被分为应用表示、应用处理和数据管理功能。这些功能由称为层的独立组件提供服务。应用层允许职责分离。各层之间的所有通信都是线性的。每一层都由自己的软件子系统管理。 n 层部署提供了改进应用可伸缩性的好处。整体式应用通常部署为三层(见图 1-2 )应用。
img/497627_1_En_1_Fig2_HTML.jpg
图 1-2
三层
表示层:这一层负责提供应用的所有静态内容。它通常通过使用 Apache、Nginx 和 IIS 等 web 服务器来管理。这些 web 服务器不仅为应用提供静态 UI 组件,还通过将请求路由到应用层来处理动态内容。Web 服务器经过优化,可以处理许多静态数据请求。因此,在负载下,它们表现良好。其中一些服务器还提供不同的负载平衡机制。这些机制可以支持应用层的多个节点。
应用层:该层负责提供所有的处理功能。它包含交付应用核心功能的业务处理逻辑。开发团队负责在合适的技术堆栈(如 Java、Python 和. NET)中构建这一层。这一层能够为用户请求提供服务,并生成适当的动态响应。它接收来自表示层的请求。为了满足请求,应用层可能需要额外的数据来与数据层交互。
数据层:该层提供数据存储和数据检索的能力。这些数据管理功能超出了应用的范围。因此,应用使用数据库来满足这些需求。数据层使用 API 提供数据操作功能。应用层调用这个 API。
使用三层架构有很多好处,包括可伸缩性、性能和可用性。您可以在不同的机器上部署这些层,并以优化的方式使用可用资源。应用层提供了大部分的处理能力。因此,它需要更多的资源。另一方面,web 服务器提供静态内容,不需要很多资源。这种部署模式通过对每一层采用不同的复制策略来提高应用的可用性。
四层部署
三层部署与 monolith 应用一致。整体通常是应用层。但是有了微服务,整块被转换成几个服务。因此,三层部署模型不足以处理微服务架构。它需要以下四层部署模型(见图 1-3 )。
img/497627_1_En_1_Fig3_HTML.jpg
图 1-3
四层
内容交付层:这一层负责将内容交付给最终用户。客户端可以在 web 浏览器或移动应用中使用应用。它经常要求在不同的平台上制作不同的用户界面。内容交付层负责确保应用 UI 在这些不同的平台上正常工作。该层还抽象了服务层,并允许开发人员快速开发新的服务来满足不断变化的业务需求。
网关层:这一层有两个角色。
动态发现已部署的服务,并将它们与用户请求相关联
将请求路由到服务并发送响应
对于每个请求,网关层从所有底层服务接收数据,并发回一个聚合响应。它必须处理不同的场景,比如基于角色的访问、延迟响应和错误响应。这些行为使得服务层更容易。服务层可以只关注业务需求。
服务层:这一层负责提供所有的业务功能。服务层是为微服务方法设计的。该层向其客户端提供数据,而不关心数据是如何被使用的。客户端可以是其他服务或应用 UI。每个服务都可以基于它们的请求负载模式进行扩展。客户端有责任确定新的实例。所有这些都支持应用生态系统的可插拔方法。新服务可以通过使用现有服务来构建。它们可以很容易地集成到企业环境中。
数据层:该层提供数据存储和数据检索的能力。数据管理功能仍然超出了应用的范围。但是每项服务都有专属的数据管理基础设施。可以是 MySQL 这样的 DBMS,也可以是 Mongo 这样的文档库。
四层架构(见图 1-3 )是由早期的微服务采用者如网飞、亚马逊和 Twitter 开创的。在范例的中心,网关层负责将完整的解决方案绑定在一起。网关需要一个能够将其余层链接在一起的解决方案,以便所有层都能够通信、扩展和交付。在三层架构中,表示层拥有可用于网关层的 web 服务器。但是首先,您应该确定网关层解决方案所需的特征。
网关特征
网关是所有用户流量的入口点。它通常负责将请求委托给不同的服务,整理它们的响应,并将其发送回用户。在微服务架构下,网关必须与架构的动态特性协同工作。以下部分讨论网关组件的不同特征。
应用层协议
OSI 网络模型在第 4 层和第 7 层处理流量。第 4 层仅提供底层连接细节。这一层的流量管理只能使用协议(TCP/UDP)和端口细节来执行。另一方面,第 7 层在应用层运行。它可以根据每个消息的实际内容执行流量路由。HTTP 是使用最广泛的应用协议之一。您可以检查 HTTP 头和正文来执行服务路由。
第 7 层负载平衡使负载平衡器能够做出更明智的负载平衡决策。它可以应用各种优化,比如压缩、连接重用等等。您还可以配置缓冲来卸载上游服务器的慢速连接,以提高整体吞吐量。最后,您可以应用加密来保护我们的通信。
在当前的生态系统中,有各种各样的应用协议可供选择。这些协议中的每一个都满足一组需求。团队可能会采用特定的应用协议,比如 gRPC,因为它更适合他们的微服务。这不需要其他团队适应相同的应用协议。但是在生态系统中,网关需要将流量分配给大多数服务。因此,它需要支持所需的协议。应用协议的列表非常广泛。因此,网关需要有一组丰富的当前协议。此外,通过添加新的协议来扩展这个列表应该很容易。
PROTOCOLS
HTTP/2 是 HTTP/1.1 协议的下一个版本。它是一个二进制协议,不会改变任何现有的 HTTP 语义。但它提供实时多路通信,并通过更好地利用底层 TCP 连接来提高应用性能。
gRPC 是一个二进制 RPC 协议。它提供了各种功能,例如多路复用、流、健康指标和连接池。它通常与 JSON 或协议缓冲区等有效负载序列化一起使用。
REST (表述性状态转移)是基于 HTTP 语义的应用协议。该协议表示使用 HTTP 方法访问的资源。它通常与 JSON 有效负载一起使用来描述状态。
另一个重要方面是进程间通信范式。传统上,我们基于 HTTP 创建同步应用。但是对于数据驱动的微服务,您可能希望采用异步模型,如 ReactiveX 和 Zeromq。网关组件需要支持这两种形式的通信。开发人员应该能够挑选适合他们应用的模型。
动态配置
在 monolith 应用中,您知道后端应用的位置。位置不会经常改变,并且在运行时不会创建应用的更多实例。因为大多数服务器都是已知的,所以在静态配置文件中提供这些细节更容易。
但是在微服务应用中,这种解决方案不起作用。第一个挑战来自微服务的数量。通常,在开始时有有限的服务。但是随着系统的增长,人们意识到每个业务功能可以有多个细粒度的服务。通常这个数字可以增长到几百个服务。为每个服务分配一个静态地址并在静态文件中维护相同的更新是一项艰巨的任务。
第二个挑战来自微服务提供的可扩展性。服务可以在加载期间复制。当负载减轻时,这些服务将被删除。微服务的这种运行时行为会随着生态系统中服务的数量而成倍增加。在静态配置文件中跟踪所有这些变化是不可能的。
为了解决发现问题,微服务架构提倡服务注册。它是一个包含服务实例的网络位置的数据库。服务注册表需要近乎实时地更新。它需要在新位置可用时尽快反映出来。服务注册中心需要具有高可用性。因此,它由复制数据以保持一致性的节点群集组成。
SERVICE REGISTRY PROVIDERS
下面是最广泛使用的服务注册中心提供者。
Eureka 是一个基于 REST 的解决方案,用于注册和查询服务实例。网飞开发的解决方案是其微服务之旅的一部分。它经常在 AWS 云中使用。
etcd 是一个高度可用、分布式、一致的键值存储。它用于共享配置和服务发现。Kubernetes 使用 etcd 进行服务发现和配置存储。
Consul 是由 Hashicorp 创建的用于发现和配置服务的解决方案。除了服务注册之外,Consul 还提供了广泛的功能,比如健康检查和锁定。Consul 提供了一个 API,允许客户端注册和发现服务。
Apache Zookeeper 是为 Hadoop 生态系统而创建的。它是分布式应用的高性能协调服务。馆长是一个 Java 库,创建于 Zookeeper 之上,提供服务发现特性。
网关组件需要与服务注册中心进行交互。它可以尝试轮询服务注册表,但这样效率不高。或者,服务注册中心需要将更改推送到网关。网关需要选择这些更改并重新配置自己。因此,总而言之,网关需要与注册中心很好地集成。
热重装
在微服务架构中,部署了许多服务。这些现有服务中的每一个都可以被更新,或者可以添加新的服务。所有这些更改都需要传播到网关层。此外,网关组件可能需要一些升级来解决问题。这些操作必须在不影响最终用户的情况下执行。甚至几秒钟的停机时间都是有害的。如果网关需要停机进行服务更新,那么停机时间将乘以服务更新的频率。总之,这可能导致频繁的服务中断。
网关组件应该处理所有更新,而不需要任何重启。它不能对配置更新或升级进行任何区分。
可观察性
可观性是从控制理论中借来的概念。它是在系统之外了解系统状态的过程。它包含了诊断故障所需的所有信息。微服务中的可观测性完全不同于整体系统中的可观测性。在 monolith 应用中,有一个包含以下日志的三层部署。
请求日志
应用日志
错误日志
您可以连接回日志来确定(相当准确地)系统一直在执行什么。但是在微服务架构中,您需要跟踪数十或数百种不同的服务。仅使用日志来预测应用状态不再可能。为此,我们需要新的机制。微服务架构推荐以下方法。
追踪
请求跟踪是一种分析和监控分布式架构(如微服务)的方法。在微服务中,一个用户请求通常由多个服务处理。这些服务中的每一个都执行其各自的处理。所有这些都以请求跨度的形式记录下来。一个请求的所有这些跨度被组合成整个请求的单个跟踪。因此,请求跟踪显示了每个服务为特定请求花费的时间。
任何服务失败都可以很容易地在请求跟踪中看到。该跟踪还有助于确定性能瓶颈。跟踪是调试应用行为的一个很好的解决方案,但是它是以牺牲一致性为代价的。所有服务都必须传播正确的请求垃圾邮件。如果服务不提供 span 或者通过忽略现有报头来重新生成 span,则结果请求跟踪不能捕获所述服务。
网关组件接收来自微服务生态系统外部的所有流量。它可以跨不同的服务分发请求。因此,网关组件需要为跟踪生成请求范围。
韵律学
微服务最佳实践建议生成可分析的应用指标。这些指标反映了我们服务的状态。随着时间的推移,收集指标有助于分析和改进服务性能。在失败场景中,度量有助于确定根本原因。应用级指标可以包括排队入站 HTTP 请求的数量、请求延迟、数据库连接和缓存命中率。应用还必须创建特定于其上下文的自定义指标。网关组件还必须导出可以捕获的相关指标。这些指标可以是不同应用协议的状态代码,如 HTTP(2XX、4XX、3XX、5XX)、服务错误率、请求队列等。
总之,网关组件需要提供各种各样的可观察性输出。它必须导出统计数据、指标和日志,以便与微服务架构中的监控解决方案集成。
TLS 终止
数据安全性通常是系统的非功能性需求。应用已经使用 TLS 通信实现了数据安全。TLS 通信允许使用私有-公共密钥对加密/解密数据。在网关或表示层终止 TLS 的过程使应用能够更好地执行,因为应用不必自己处理加密和解密。这在传统架构中运行良好,因为进程间网络调用很少。但是在微服务架构中,许多服务都在生态系统中运行。根据安全标准,服务之间未加密的通信会带来严重的风险。因此,作为最佳实践,您需要加密整个集群中的所有网络通信。
服务授权是微服务架构中的另一个挑战。在微服务中,更多的请求是通过网络发出的。服务需要确定哪个客户端正在进行调用。这有助于在客户端服务出现故障时设置限制。把这些控制作为一个胭脂服务是必要的,并在系统中肆虐。身份可以通过多种方式建立。客户端可以传递不记名令牌,但是这个过程已经过时了。潜在的攻击者可以捕获和传递不记名令牌。作为最佳实践,您希望确保客户端仅使用不可移植的身份进行身份验证。因此,相互 TLS (mTLS)身份验证是推荐的做法。对于相互验证身份的服务,在建立连接之前,它们都需要提供对方信任的证书和密钥。客户端和服务器提供和验证证书的这一动作被称为相互 TLS。它确保强服务标识作为进程间通信的一部分得到实施和交换。因此,网关组件需要具有双重行为。
来自外界的流量的 TLS 终止
使用相互 TLS 调用不同服务的 TLS 身份
其他功能
网关组件执行反向代理和负载平衡器的双重职责。它必须为高级负载平衡技术提供支持。此外,该组件需要支持以下特性。
超时和重试次数
限速
断路
阴影和缓冲
基于内容的路由
特征列表是不受限制的。负载平衡器通常使用 IP 地址标记、识别和定位来实现各种安全功能,如身份验证和 DoS 缓解。网关组件也必须满足所有这些需求。
我们已经讨论了网关解决方案的高级行为。这些需求是 Apache、Nginx 和 HAProxy 等成熟市场产品的愿望清单。这些服务器提供了对一些特性的支持,但是有些特性必须使用变通方法来处理。总之,这些久经考验的解决方案没有对微服务架构的一流支持。这些产品在十年前就已经开发了它们的架构,当时需求列表是不同的。下一节将讨论 Traefik,这是一个为处理微服务部署需求而创建的开源产品。
Traefik
Traefik 是一个开源 API 网关。它旨在简化微服务操作的复杂性。Traefik 通过执行服务的自动配置来达到同样的目的。根据产品文档,开发人员应该负责开发和部署服务。Traefik 可以用合理的默认值自动配置自己,并向所述服务发送请求。
如今的微服务有着不断变化的需求。Traefik 通过遵循可插拔架构来支持所有这些需求。它支持每一种主要的集群技术,如 Kubernetes、Docker、Docker Swarm、AWS、Mesos 和 Marathon(见图 1-4 )。所有这些引擎都有自己的集成点,也称为提供者。不需要维护静态配置文件。提供者负责连接到编排引擎,并确定在其上运行的服务。然后,它将此信息传递回 Traefik 服务器,后者可以将此信息应用于其路由。Traefik 能够同时与多个提供商集成。
img/497627_1_En_1_Fig4_HTML.jpg
图 1-4
Traefik
Traefik 是以 Unix 为中心开发的。它是在戈朗建造的。它提供了一个公平的表现。它遇到了一些内存问题。但是有一个很大的活跃开发人员社区在开发 Traefik。Traefik 提供的整体性能稍逊于 Nginx 等成熟的市场领导者,但它通过为所有微服务功能提供一流的支持弥补了这一点。
Note
Traefik 拥有超过 25K 个 GitHub stars(在撰写本文时),使其成为最受关注的项目之一。
装置
Traefik 经常被释放。这些版本的二进制工件可以在项目发布页面上获得( https://github.com/containous/traefik/releases )。该产品针对每种受支持的操作系统和架构发布。在撰写本文时,Traefik 2.2.0 是最新发布的版本(见图 1-5 )。
img/497627_1_En_1_Fig5_HTML.jpg
图 1-5
trafik 发布页面
在本章的剩余部分,我们将使用 macOS 版本,但是您可以使用以下任何方法下载合适的版本。
打开 https://github.com/containous/traefik/releases ,点击释放。traefik_v2.2.0_darwin_amd64.tar.gz
对终端执行curl命令:curl -o https://github.com/containous/traefik/releases/download/v2.2.0/traefik_v2.2.0_darwin_amd64.tar.gz
解压存档:tar -zxvf traefik_v2.2.0_darwin_amd64.tar.gz
该文件夹应该包含traefik可执行文件,以及另外两个文件。
$ ls -al
total 150912
-rw-rw-r--@ 1 rahulsharma staff 551002 Mar 25 22:38 CHANGELOG.md
-rw-rw-r--@ 1 rahulsharma staff 1086 Mar 25 22:38 LICENSE.md
-rwxr-xr-x@ 1 rahulsharma staff 76706392 Mar 25 22:55 traefik
单个二进制文件在跨不同平台工作时提供了简化的体验。现在让我们学习如何使用 Traefik。
trafik 命令行
Traefik 可以通过调用traefik命令来启动。该命令可以执行以下任何操作。
根据提供的配置配置 trafik
确定 trafik 版本
对 Traefik 执行运行状况检查
理解traefik命令如何支持这些行为是很重要的。在traefik命令中提供了几个参数。一旦我们到达相关的主题,你将与他们一起工作。首先,让我们通过执行以下命令来验证 Traefik 的版本。
$ ./traefik version
Version: 2.2.0
Codename: chevrotin
Go version: go1.14.1
Built: 2020-03-25T17:17:27Z
OS/Arch: darwin/amd64
该输出不仅显示了 Traefik 版本,还显示了与平台相关的信息以及创建 Traefik 二进制文件的日期。一般来说,traefik命令的语法如下。
traefik [sub-command] [flags] [arguments]
在这个命令中,所有参数都是可选的。现在,您可以通过调用命令来配置 Traefik。请务必注意,Traefik 配置可以通过以下方式提供。
配置文件
用户指定的命令行标志
系统环境变量
它们按列出的顺序进行评估。如果未指定值,Traefik 将应用默认值。您可以在不传递这些值的情况下执行该命令。
$ ./traefik
INFO[0000] Configuration loaded from flags.
这个输出告诉您 Traefik 已经启动。它配置了基于标志的配置。该命令开始监听端口 80。现在让我们通过对 http://localhost/执行 cURL 来验证这一点。
$ curl -i http://localhost/
HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Fri, 01 May 2020 16:16:32 GMT
Content-Length: 19
404 page not found
cURL 请求从服务器获得 404 响应。在下一章讨论入口点、路由器和服务时,我们将再次讨论配置细节。
Traefik API
Traefik 还提供了 REST API,它可以访问 Traefik 中所有可用的信息。表 1-1 描述了几个主要的终点。
表 1-1
trafik 中的 api 端点
|
端点
|
描述
|
| --- | --- |
| /API/版本 | 提供了有关 trafik 版本的信息 |
| /API/概述 | 提供有关 HTTP 和 TCP 的统计信息,以及启用的功能和提供程序 |
| /API/entry tips | 列出所有入口点信息。 |
| /API/http/服务 | 列出所有 HTTP 服务信息 |
| /API/http/路由器 | 列出所有 HTTP 路由器信息 |
| /API/http/中间件 | 列出所有 HTTP 中间件信息 |
api 的完整列表可在 traefik.io/v2.2/运营/API/#端点获得。默认情况下,API 是禁用的。需要通过传递适当的标志来启用它。您可以通过使用api.insecure标志启动 Traefik 来激活 API,这会将 REST API 部署为 Traefik 端点。
rahulsharma$ ./traefik -api.insecure true
INFO[0000] Configuration loaded from flags.
现在让我们在浏览器中查找http://localhost:8080/api/overview。输出显示从 API 返回的统计数据(见图 1-6 )。
img/497627_1_En_1_Fig6_HTML.jpg
图 1-6
API 概述输出
该行为也可以通过使用TRAEFIK_API_INSECURE环境变量来实现。环境变量相当于api.insecure标志。让我们通过设置环境变量再次运行该命令。
rahulsharma$ export TRAEFIK_API_INSECURE=True
rahulsharma$ ./traefik
INFO[0000] Configuration loaded from environment variables.
Note
不建议在生产系统上启用 API。API 可以公开完整的基础设施和服务细节,包括敏感信息。
前面的命令以不安全的模式部署了 Traefik API。不建议这样做。Traefik 应该通过身份验证和授权来保护。此外,API 端点应该只能在内部网络中访问,而不能暴露给公共网络。本书将在后续章节中介绍这些实践。
交通控制面板
Traefik API 开箱后带有一个仪表盘(参见图 1-7 )。仪表板仅供查看。它显示 Traefik 中配置的所有组件的状态。仪表板还显示每个部署的组件的执行情况。仪表板是一个直观的表示,运营团队可以使用它来进行监控。一旦你以一种不安全的方式启动了 Traefik,查一下http://localhost:8080/dashboard#/。
img/497627_1_En_1_Fig7_HTML.jpg
图 1-7
交通控制面板
仪表板显示 TCP 和 UDP 服务。基于 HTTP 的应用有两个监听端口。仪表板还捕获每个服务的错误率。
摘要
在本章中,您了解了微服务的采用如何改变了网关的要求。我们讨论了网关组件的各种预期行为。像 Nginx 和 HAProxy 这样的成熟市场产品已经尝试适应这些功能。但是这些产品已经无法提供所有需求的一流支持。Traefik 就是为了满足这些需求而构建的。有多种方法可以配置 Traefik。可以从文件、参数或环境变量传递配置。对于所有未指定的配置,它都有合理的默认值。最后,您了解了 Traefik 中可用的 API 和仪表板。既然我们已经部署了 Traefik,让我们在下一章中配置它来处理一些端点。
二、配置 Traefik
在上一章中,您了解了微服务的采用如何改变了对网关组件的行为要求。Traefik 就是为了满足所有这些需求而构建的。配置 Traefik 有多种方法;配置可以通过文件、参数或环境变量传递。对于所有未指定的配置,它都有合理的默认值。您还了解了 Traefik 中可用的 API 和仪表板。在本章中,让我们在您离开的地方继续深入研究配置 Traefik 以公开几个端点的各种方法。
本章涵盖了路由的基础知识。它讨论了路由中使用的各种组件。我们将介绍一个贯穿本章的小示例应用。Traefik 路由配置用于向外界公开该应用。在本章中,您将尝试手动配置 Traefik 来公开一个简单的服务。在后面的章节中,您可以在此基础上利用 Traefik 的自动配置功能。
配置主题
让我们介绍以下 Traefik 配置,以在 Traefik 网关上公开一个示例应用。
入口点
提供者
路由器
规则
中间件
服务
这些配置件之间的相互作用如图 2-1 所示。我们会在您前进的过程中详细描述每一项。
img/497627_1_En_2_Fig1_HTML.jpg
图 2-1
trafik 配置架构
示例 Web 服务简介
在继续进行 Traefik 配置之前,让我们看一下要在 Traefik 中公开的示例应用。在本章中,我们使用这个简单的 web API 来通过 Traefik 提供流量服务。
这个简单的服务是用 Go 编写的(如清单 2-1 所示),它监听 HTTP 端口 9080。它在默认路径“/”上返回一个“Hello World”字符串。
main.go
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", handler)
log.Println("Server listening on port 9080...")
log.Fatal(http.ListenAndServe(":9080", nil))
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World")
}
Listing 2-1Simple Web API Written in Go
Note
您需要在系统上安装 Go 1.1.3 来运行此示例。然而,除此之外,它不需要任何其他特殊要求。关于 Go 安装和执行的讨论超出了本书的范围;但是,官方的 Go 文档非常全面。
在上一章中,您看到了如何在机器上以独立模式安装和设置 Traefik。在这里,您使用 Traefik CLI。本章所有的例子都是在 macOS 上运行的。但是,如果您安装了 Go 和 Traefik CLI,您应该能够很容易地理解。
当您运行 Go 服务时,您会看到清单 2-2 中的控制台消息。
➜ hello-world-service> go run main.go
2020/05/02 20:34:26 Server listening on port 9080...
Listing 2-2Running the Go web API
当您在浏览器中打开该 URL 时,您会看到以下内容(参见图 2-2 )。
img/497627_1_En_2_Fig2_HTML.jpg
图 2-2
打开 URL http://localhost:9080/的浏览器截图
您会在命令行上看到下面的curl(参见清单 2-3 )。
➜ hello-world-service> curl localhost:9080
Hello, World
Listing 2-3Testing the API endpoint with curl
在本章的其余部分,我们将运行这个示例应用并配置 Traefik 来公开这个应用的端点。在此过程中,我们将探索各种 Traefik 配置。
img/497627_1_En_2_Fig3_HTML.jpg
图 2-3
在 Traefik 上公开“Hello World”Go 服务
Note
对于本章的其余部分,术语上游和下游描述了与消息流相关的方向要求:所有消息从上游流向下游。术语入站和出站描述了与请求路由相关的方向要求:入站意味着朝向原始服务器,而出站意味着朝向用户代理。这符合 HTTP 规范 RFC 7230 第 2.3 节( https://tools.ietf.org/html/rfc7230#section-2.3 )。
流量配置
Traefik 有两种方式提供配置:静态和动态(见图 2-4 )。
img/497627_1_En_2_Fig4_HTML.jpg
图 2-4
Traefik 中的不同配置类型
静态配置在启动时通过以下机制提供。一旦 Traefik 启动,它就不会改变。
文件
硬币指示器 (coin-levelindicator 的缩写)命令行界面(Command Line Interface for batch scripting)
环境变量
对于静态配置,首先通过 CLI 配置 Traefik,然后是环境变量,最后是静态文件配置。
您可以在静态配置(文件或 CLI)中定义入口点。Entrypoints 是 Traefik 侦听传入 TCP/UDP 流量的端口的端口定义。
提供者是难题的另一部分,必须在静态启动配置中指定。提供商赋予 Traefik 强大的功能。动态配置(如路由器和服务)在运行时被更改和刷新,并通过提供者进行配置。Traefik 可以通过一组预配置的提供者与您的服务目录对话,而不必手动配置每个下游服务。有 Docker、Kubernetes 和 Consul 的提供者,也有诸如 files 之类的存储和诸如 etcd 之类的键值存储。这允许 Traefik 自动在边缘公开下游服务。在本章中,您将通过手动使用 FileProvider 来指定动态配置。在后面的章节中,您将深入了解 Traefik 如何与其他提供者集成,比如 Consul 和 Kubernetes。
入口点
Traefik 配置定义了一组侦听传入请求的入口点(或端口号)。这些入口点可以服务于 HTTP、TLS、gRPC 或 TCP 流量。您可以为希望通过 Traefik edge gateway 公开的每个后端服务定义一个入口点。入口点定义了地址、协议、报头、传输细节(如超时)和 TLS 细节的底层细节。
在开始定义入口点之前,让我们先回顾一下您希望如何运行 Traefik,并确定您如何在运行时观察已定义的配置。您可以从命令行启动 Traefik,无需任何其他配置。为了在后续步骤中观察配置的结果,您还可以通过传递'--api.dashboard=true'标志来启用 Traefik web 仪表板。
使用 cli 参数启动流量
➜ ~ ./traefik --api.dashboard=true --api.insecure=true
INFO[0000] Configuration loaded from flags.
Listing 2-4Start Traefik with command line arguments
清单 2-4 在本地主机的端口 8080 上启动 Traefik,Traefik 仪表板以默认配置显示。仪表板位于/api/dashboard/ route 下。
默认情况下,Traefik 建议在安全模式下公开仪表板;因为您现在只想看到仪表板,而不需要太多的配置,所以您用'--api.insecure=true'标志在不安全模式下启动它。请注意,对于生产用例,不建议这样做。
正如您在图 2-5 中看到的,Traefik 仪表板有一组区域,允许您观察在 Traefik 中配置和启用的所有功能。目前,Traefik 实例中只公开了默认配置。
img/497627_1_En_2_Fig5_HTML.jpg
图 2-5
在http://localhost:8080/打开 Traefik 仪表板的屏幕截图
默认情况下定义了两个入口点。
端口 80 上侦听的默认 HTTP 入口点
一个名为 Traefik 的入口点侦听端口 8080,它提供对仪表板的访问
仪表板不通过任何特殊机制公开。Traefik 使用标准的入口点配置公开它。在入口点下面,您可以看到按协议(HTTP、TCP 和 UDP)分类的路由器、中间件和服务。随着您向前移动,您可以深入查看您在控制面板上看到的这些进一步的部分。
如您所见,Traefik 定义了许多入口点、路由器、中间件和服务。下一步是使用这些相同的机制公开下游服务。
如您所见,当您启用 web 仪表板时,默认情况下 Traefik 已经监听端口 80(默认 HTTP)和 8080(仪表板 HTTP)。
让我们定义自己的入口点来公开已经通过 Traefik 讨论过的“Hello World”Go 服务。
首先,您需要定义一个入口点。这是在静态配置中启动时使用以下方法之一完成的。
CLI 参数
环境变量
当前目录下的配置文件 traefik.yml
使用 cli 定义的入口点启动流量
➜ ~ ./traefik --api.dashboard=true --api.insecure=true --entryPoints.web.address=:80
INFO[0000] Configuration loaded from flags.
Listing 2-5Traefik entrypoint defined through command line argument
清单 2-5 定义了一个名为 web 的入口点,它监听端口 80。这将覆盖您之前在端口 80 上看到的默认 HTTP 入口点(参见图 2-6 )。
img/497627_1_En_2_Fig6_HTML.jpg
图 2-6
Traefik 仪表板中的入口点于http://localhost:8080/打开
当您尝试从 cURL 访问本地主机上的这个端口时,您会看到清单 2-6 中的输出。Traefik 正在侦听此端口,但是后端还没有连接到此端口的服务。
➜ hello-world-service> curl localhost
404 page not found
Listing 2-6Testing the localhost 80 port with curl
我们使用命令行参数定义静态配置,成功地公开了入口点。接下来,让我们尝试使用环境变量做同样的事情。
使用环境变量中定义的入口点启动 trafik
您在终端中执行以下命令来启动 Traefik,在端口 80 上公开一个名为 web 的入口点(参见清单 2-7 )。
➜ traefik-config> export TRAEFIK_API_DASHBOARD=true
➜ traefik-config> export TRAEFIK_API_INSECURE=true
➜ traefik-config> export TRAEFIK_ENTRYPOINTS_WEB_ADDRESS=":80"
➜ traefik-config> traefik
INFO[0000] Configuration loaded from environment variables.
Listing 2-7Starting Traefik with entrypoint configuration in environment variables
当您在浏览器中访问该端点时,您会看到与之前相同的结果(参见图 2-7 )。
img/497627_1_En_2_Fig7_HTML.jpg
图 2-7
还没有后端服务连接到端口 80
命令行参数和环境变量可以随意使用;但是,对于本章的其余部分,您将通过文件提供所有配置。这是配置 Traefik 的推荐方式,因为文件配置简单,不容易出现打字错误和错误。在源代码控制中也可以很容易地跟踪它,支持更多的 GitOps 模型。接下来,让我们看看如何实现这一点。
用当前目录中的配置文件 Traefik.yml 定义的入口点
Traefik 静态配置文件可以通过多种方式提供。
当前目录中的 Traefik.yml 文件
Traefik.yml file in $HOME/.config
作为命令行参数传入 CLI 的文件的位置
--configFile=path/to/traefik-static-config.yml
为简单起见,在本章的剩余部分,我们将 Traefik 配置文件限制在当前目录中。
TOML 诉 YAML 案
在 Traefik 中有两种定义文件配置的竞争格式:TOML 和 YAML。
虽然 Traefik 团队更喜欢 TOML,但本章中的所有配置示例都在 YAML。TOML 是一种不太知名和晦涩的格式,而 YAML 在各种平台中得到广泛支持,并且是 Docker 和 Kubernetes 生态系统的默认声明性状态配置格式。不要学习定义简单配置的新格式,让我们坚持使用 YAML 来满足所有需求。在 Traefik 配置的所有问题上,YAML 与 TOML 拥有同等的功能。
当前目录下的 traefik.yml 文件指定了入口点,如清单 2-8 所示。您还可以在此文件中添加配置来启用仪表板,而不是在命令行中启用它。
Entrypoints have to be defined as static configuration in traefik.yml
entryPoints:
web:
address: ":80"
api:
insecure: true
dashboard: true
Listing 2-8Traefik Static YAML Configuration
为了比较,在清单 2-9 中定义了 TOML 格式的相同配置。你不用这个;它只是被包括在内以供参考。
Entrypoints have to be defined as static configuration in traefik.yml
[entryPoints]
[entryPoints.web]
address = ":80"
[api]
insecure = true
dashboard = true
Listing 2-9Traefik Static TOML Configuration
现在让我们用静态 YAML 配置启动 Traefik。
➜ traefik-config> ./traefik
INFO[0000] Configuration loaded from file: /Users/akshay/traefik-book/traefik-config/traefik.yml
Listing 2-10Startup Traefik with static file configuration
当您启动 Traefik 时(参见清单 2-10 ,它会自动提取当前目录中的 traefik.yml 文件。这样做的结果与前两个小节中看到的结果相同。
路由器
对于 Traefik 公开的每个入口点,必须连接相应的路由器来进一步路由流量。路由器由两部分组成。
一套规则。入口点上的每个传入请求都与这组规则相匹配。
一套中间件。规则匹配的每个请求都可以使用相应的中间件进行转换。中间件是执行认证和速率限制的所有专用网关功能的地方。
既然入口点已经公开,那么使用路由器配置将后端 Go API 连接到入口点。让我们首先来看看 Traefik 仪表板中定义的默认路由器。
默认情况下定义了两个 HTTP 路由器。当您点击主页上路由器部分的“浏览->”时,您会看到如图 2-8 所示的内容。
img/497627_1_En_2_Fig8_HTML.jpg
图 2-8
深入了解默认 HTTP 路由器。
在“/api”下定义的第一个路由是名为 api@internal 的默认父路由。
“/”下的第二条路线是名为“dashboard@internal”的仪表板路线。
您可以进一步深入这些路由器并检查它们的详细信息(参见图 2-9 和 2-10 )。这些路由分别服务于 API 和仪表板的流量。
img/497627_1_En_2_Fig10_HTML.jpg
图 2-10
仪表板@内部 HTTP 路由器的详细视图
img/497627_1_En_2_Fig9_HTML.jpg
图 2-9
api @内部 HTTP 路由器的详细视图
您必须定义 HTTP 路由器,以便在 Traefik 仪表板上显示类似的内容。从现在开始,您将利用 FileProvider 来指定动态配置。Traefik 可以通过支持的提供者与您平台的服务发现机制对话。对于这个简单的用例,您在一个文件中指定所有的配置,这很容易被 Traefik 支持。路由器、中间件和服务的所有配置都是通过动态配置指定的。有两种方法可以指定 FileProvider 配置。
通过文件名指定的单个文件
配置文件的整个目录,这是生产中推荐的方法,因为您可以将各种配置分成多个文件
在清单 2-11 中,您在存放所有动态配置的当前目录中指定了一个文件名。然后 Traefik 监视该文件是否有任何更改,并且在 Traefik 中自动刷新配置。
Entrypoints have to be defined as static configuration in traefik.yml
entryPoints:
web:
address: ":80"
providers:
file:
filename: "traefik-dynamic-conf.yml"
watch: true
api:
insecure: true
dashboard: true
Listing 2-11Dynamic configuration file name defined in static cofiguration
路由器规则
在 traefik-dynamic-conf.yml 动态配置文件中,首先定义 HTTP 路由器及其路由规则(参见清单 2-12 )。
http:
routers:
router0:
entryPoints:
- web
service: hello-world
rule: Path(`/hello-world`)
Listing 2-12traefik-dynamic-conf.yml dynamic configuration file
一旦所有的配置都设置好了,您就可以在同一个目录中运行 Traefik CLI(参见清单 2-13 )。
➜ traefik-config> ./traefik
INFO[0000] Configuration loaded from file: /Users/akshay/code/k8s/traefik-book/traefik-config/traefik.yml
ERRO[2020-05-13T09:22:17+05:30] the service "hello-world@file" does not exist entryPointName=web routerName=router0@file
ERRO[2020-05-13T09:22:18+05:30] the service "hello-world@file" does not exist entryPointName=web routerName=router0@file
Listing 2-13Running Traefik with router rule in dynamic configuration file
您定义了一个规则来将请求路径(/hello-world)匹配到一个尚未配置的后端服务,因此 Traefik 在启动时会抛出一个控制台错误。您会在仪表板中看到类似的错误(参见图 2-11 )。HTTP 路由器部分显示一个错误。同样有趣的是页面底部的 Providers 部分,它现在有一个 FileProvider 条目。
img/497627_1_En_2_Fig11_HTML.jpg
图 2-11
带有 HTTP 路由器的仪表板视图
路由器的下一块要配置的应该是中间件;然而,我们将暂时跳过它,稍后再回来。首先,您配置服务后端并获得端到端的通信。
服务
服务定义了请求必须被路由到的实际目标。它们是您希望通过 Traefik 公开的实际 API 端点。请注意,服务类型必须与路由器类型匹配(例如,HTTP 路由器只能连接到 HTTP 服务)。在匹配和转换请求之后,路由器将它们转发到您希望公开的服务。
在定义服务之前,让我们看看 Traefik 仪表板中配置的默认服务。定义了三种 HTTP 服务。当你点击“服务”部分的“浏览->”时,你会看到如图 2-12 所示的内容。
img/497627_1_En_2_Fig12_HTML.jpg
图 2-12
仪表板默认服务
您可以进一步深入这些服务。api@internal service 处理 Traefik API 请求(参见图 2-13 )。
img/497627_1_En_2_Fig13_HTML.jpg
图 2-13
仪表板默认服务 api@internal
仪表板@内部服务(见图 2-14 )处理仪表板请求。当启用 API 和仪表板时,Traefik 会隐式注册这两个服务。
img/497627_1_En_2_Fig15_HTML.jpg
图 2-15
仪表板默认服务 noop@internal
img/497627_1_En_2_Fig14_HTML.jpg
图 2-14
仪表板默认服务仪表板@内部
还有一个 noop@internal(如图 2-15 所示)用于重定向。
清单 2-14 在 traefik-dynamic-conf.yml 中定义了一个后端服务,以满足上一节中设置的规则,将流量路由到 Go“Hello World”服务。请记住,该服务运行在本地主机端口 9080 上。
Dynamic configuration
http:
routers:
router0:
entryPoints:
- web
service: hello-world
rule: Path(`/hello-world`)
services:
hello-world:
loadBalancer:
servers:
- url: http://localhost:9080/
Listing 2-14Service configuration in dynamic configuration file
您现在启动 Traefik 并尝试在子路径'/hello-world '上的 Traefik 端口 80 上访问 Go 服务。当您使用 curl 时,您会看到清单 2-15 中的输出。
➜ traefik-config> curl localhost/hello-world
Hello, World
Listing 2-15Access service endpoint on localhost with curl
现在,您已经成功地在 Traefik 的端口 80 上公开了后端服务。让我们看看 Traefik 仪表板中显示了什么。您可以在主仪表板页面上看到 HTTP 路由器、服务和中间件(参见图 2-16 )。
img/497627_1_En_2_Fig16_HTML.jpg
图 2-16
路由器和服务已配置
您可以深入到 HTTP 服务页面,在这里您会看到 hello-world 服务的一个新条目(参见图 2-17 )。
img/497627_1_En_2_Fig17_HTML.jpg
图 2-17
向下钻取到配置的 HTTP 服务。
然后,您可以深入到 hello-world 服务以查看其详细信息(参见图 2-18 )。这也显示了 Servers 部分中的后端服务 URL。
img/497627_1_En_2_Fig18_HTML.jpg
图 2-18
细化到已配置的 Hello World 文件 HTTP 服务
然后导航回主仪表板页面(参见图 2-19 )。
img/497627_1_En_2_Fig19_HTML.jpg
图 2-19
配置中间件之前的 trafik 仪表板
从这里,您可以深入到 HTTP 路由器(参见图 2-20 )。
img/497627_1_En_2_Fig20_HTML.jpg
图 2-20
深入查看已配置的 HTTP 路由器
然后您可以深入到 HTTP 路由器,它将 hello-world 服务附加到 web 入口点(参见图 2-21 )。
img/497627_1_En_2_Fig21_HTML.jpg
图 2-21
连接到 hello-world 服务的 HTTP 路由器
中间件
现在,您已经成功地在 Traefik 中公开了第一个服务,让我们回头向路由器添加一个中间件,以添加额外的 API 网关功能。
默认定义了两个 HTTP 中间件。当你点击中间件部分的“浏览->”时,你会看到如图 2-22 所示的内容。
img/497627_1_En_2_Fig22_HTML.jpg
图 2-22
默认中间件
您可以轻松地向下导航,查看隐式定义的中间件的详细信息。它们与仪表板一起自动启用,对于 URL 模式的特殊处理非常有用(参见图 2-23 和 2-24 )。这样,即使内部 Traefik 服务也遵循与用户定义的服务相同的配置机制。
img/497627_1_En_2_Fig24_HTML.jpg
图 2-24
默认 stripprefix 中间件
img/497627_1_En_2_Fig23_HTML.jpg
图 2-23
默认仪表板重定向中间件
对于用例,您定义内置的基本身份验证中间件来保护 Hello World API。
让我们从使用htpasswd命令行工具生成用户名-密码对开始。这是 Traefik 文档中基本验证页面上推荐的方法。如果您的系统上没有此工具,您可以使用任何其他兼容的密码哈希实用程序。你用 admin 作为用户名,用 admin@123 作为密码。用户名可以指定为纯文本,但密码必须以 MD5、SH1 或 BCrypt 格式散列提供(参见清单 2-16 )。
➜ traefik-config> htpasswd -nb admin admin@123
admin:$apr1$JsindKAS$zCWAvabJOgQvI.Dd3zjtE.
Listing 2-16Generate username password pair for authentication
您将这个值复制到 Traefik 动态配置中(参见清单 2-17 )。
Dynamic configuration
http:
routers:
router0:
entryPoints:
- web
middlewares:
- basic-auth
service: hello-world
rule: Path(`/hello-world`)
services:
hello-world:
loadBalancer:
servers:
- url: "http://localhost:9080/"
Declaring the basic auth middleware with the user credentials
middlewares:
basic-auth:
basicAuth:
users:
- "admin:$apr1$JsindKAS$zCWAvabJOgQvI.Dd3zjtE."
Listing 2-17Basic auth middleware configuration in dynamic configuration file
现在让我们试试浏览器中的/hello-world 端点。您会在浏览器中看到一个基本的授权提示,要求您输入用户名和密码(参见图 2-25 )。输入这些之后,你可以看到 hello-world 服务的响应(见图 2-26 )。
img/497627_1_En_2_Fig26_HTML.jpg
图 2-26
最终认证 Hello World
img/497627_1_En_2_Fig25_HTML.jpg
图 2-25
浏览器基本身份验证
让我们在命令行上用curl做同样的尝试(参见清单 2-18 )。
➜ traefik-config> curl localhost/hello-world
401 Unauthorized
➜ traefik-config> curl -u admin localhost/hello-world
Enter host password for user 'admin':
Hello, World
Listing 2-18Testing basic auth middleware applied on localhost endpoint
现在这已经可以工作了,让我们最后看一下 Traefik 仪表板中的配置。你可以看到主页上的 HTTP 中间件平铺显示了另一个中间件(见图 2-27 )。
img/497627_1_En_2_Fig27_HTML.jpg
图 2-27
最终配置
您可以深入到中间件页面,在那里您可以查看已定义的basic-auth@file中间件。Traefik 会自动指定名称。你还可以看到其他隐式中间件(见图 2-28 )。
img/497627_1_En_2_Fig28_HTML.jpg
图 2-28
向下钻取到已配置的 HTTP 中间件
然后进一步深入查看中间件的配置。您还可以看到您为认证定义的用户(参见图 2-29 )。
img/497627_1_En_2_Fig29_HTML.jpg
图 2-29
已配置的基本身份验证中间件
在该页面中,您可以直接导航到该中间件的关联路由器并查看其详细信息(参见图 2-30 )。
img/497627_1_En_2_Fig30_HTML.jpg
图 2-30
连接路由器、服务和中间件
摘要
在本章中,您了解了 Traefik 的基本配置,以及如何手动配置 Traefik 以在特定端口上公开 API,并将流量路由到相应的后端服务。您了解了配置 Traefik 的各种方法,并从文件、CLI 参数和环境变量传递了配置。您还深入研究了 Traefik 仪表板,以了解如何使用它来理解配置。
到目前为止,您只是简单地将流量路由到一个后备服务实例。在下一章中,您将深入了解 Traefik 针对 HTTP 和 TCP 流量的负载平衡器功能。
三、负载平衡
缩放是应用设计的一个重要原则。扩展不仅提供应用性能,而且,如果操作得当,扩展还提供应用可用性和容错能力。开发人员必须注意有效地扩展应用。不可能是后发展思想。之前,您已经了解到,通过向正在运行的实例分配更多资源,可以垂直扩展应用。单片应用遵循这一原则。第一章解释了这是一种无效的方法。
此外,为了提供可用性,我们通常选择热-冷部署模式。这导致效率低下,因为冷实例是备用实例。它仅在主应用实例关闭时激活。
另一方面,水平伸缩允许您同时运行应用的多个实例。这些实例中的每一个都可以在最低要求的硬件上运行,并为用户请求提供服务。这是在云环境中部署应用的首选机制。它通过使用热-热部署极大地提高了应用的可用性(参见图 3-1 )。
img/497627_1_En_3_Fig1_HTML.jpg
图 3-1
部署类型
一旦一个应用有多个实例,就必须配置一个负载平衡器来有效地使用它们。像 Traefik 这样的负载平衡器应用可以在开放系统互连(OSI)模型的第 4 层和第 7 层运行。在第 4 层,它充当 TCP/UDP 代理。它基于主机和端口信息工作。在第 7 层,负载平衡器查看许多属性(例如,HTTP 负载平衡可以基于主机、端口、请求路径或请求头来完成)。因此,Traefik 可以在两层执行负载平衡。
在第二章中,我们在 Traefik 中配置了 HTTP 服务。在本章中,我们将配置 HTTP 服务的负载平衡。Traefik 还提供 TCP 和 UDP 功能。我们也和他们一起工作。
HTTP 负载平衡器
为了有效地使用水平伸缩,我们需要一个负载平衡器。负载平衡器配置有应用的所有实例。当负载平衡器收到请求时,它必须将请求委派给一个已配置的实例。一些负载平衡算法可以改变请求处理的行为。每种算法都有优点和缺点,在某些情况下比其他情况下效果更好。
在前一章中,我们使用文件类型提供程序配置了 Traefik。我们为端口 80 创建了一个入口点。我们还添加了routers和services来处理传入的请求。服务配置指向应用的位置。负载平衡算法也在服务级别进行管理。一些服务可以使用特定的算法,而其他服务可以使用不同的算法。当我们查看服务配置时,它由以下块组成。
服务:定义服务器的逻辑分组,以便可以应用公共属性
服务器:定义应用的实际位置
这两个模块中的任何一个都可以在 Traefik 中配置负载平衡。在下一节中,我们将配置不同的属性来学习完整的行为。
一系列
循环调度(RR)是最简单的负载分配算法之一。该算法将请求平均分配给每个可用的实例(见图 3-2 )。它以循环的方式执行操作,没有任何优先级或偏好的概念。当服务器具有大致相同的计算能力时,循环负载平衡效果最佳。
img/497627_1_En_3_Fig2_HTML.jpg
图 3-2
请求分发
在图 3-2 中,应用中有四台服务器。该图描述了算法如何在它们之间分配传入的请求。更进一步,我们需要一个 HTTP 应用来配置循环调度。在剩下的部分中,我们将使用一个访问者日志记录应用。该应用具有以下行为。
添加客人姓名
列出最新的客人姓名
显示所有客人姓名
应用部署在多个机器上。每个应用实例都有一个名称,显示在 UI 中(见图 3-3 )。UI 帮助确定哪个实例服务于用户请求。
img/497627_1_En_3_Fig3_HTML.jpg
图 3-3
访问者日志屏幕
Traefik 服务配置由一个service和一个server模块组成。使用server模块配置循环调度。
http :
routers :
guest-router :
entryPoints :
- web
rule : Host(`localhost`)
service : guestbook-service
services :
guestbook-service :
loadBalancer :
servers :
- url : "http://192.168.1.10:9090/"
- url : "http://192.168.1.11:9191/"
下面是在前面的代码中配置的。
为localhost域配置了请求路由。该规则匹配所有传入的请求。在前一章中,您看到了验证请求 URL 位置的路径规则。这里我们基于主机名而不是请求路径来验证请求。Traefik 使用guestbook-service配置来处理请求。
server部分列出了所有可用实例的 URL。它被配置为一个值列表。
让我们运行 Traefik 配置并在浏览器中访问 http://localhost(见图 3-4 )。服务配置也可在 Traefik 仪表板上获得(参见图 3-5 )。它显示服务的完整状态。
img/497627_1_En_3_Fig5_HTML.jpg
图 3-5
循环赛的 trafik 仪表板
img/497627_1_En_3_Fig4_HTML.jpg
图 3-4
访问者的多个实例
如果您刷新浏览器几次,您会看到它是由两个实例提供的。我们可以在应用中添加一些条目。这些数据保存在底层数据库中。随后在两种情况下都会显示数据。简而言之,应用没有保持任何状态。所有状态都保存在数据库中。当整个应用是无状态的,就像一个访问者日志时,经典的循环算法就足够了。
另一方面,应用可以是有状态的。这意味着每个应用都有一些本地数据。在 web 应用中,状态是使用 HTTP 会话来维护的。为每个用户创建一个会话。会话是内存中的存储。它仍然与用户相关联。会话中可以存储的内容没有限制。应用开发人员可以存储以用户为中心的数据,如 id、最新事务和 UI 样式。当请求从一个实例路由到另一个实例时,会话信息会丢失。
粘性会话
会话粘性确保会话期间来自用户的所有请求都被发送到同一个实例。负载平衡器通过将 cookies 保存在用户请求中来实现这一点。负载平衡器为第一个用户请求创建一个 cookie。然后,它为每个后续请求引用该 cookie。在 Traefik 中,cookie 是在loadBalancer级别配置的。
Removed for Brevity
services :
guestbook-service :
loadBalancer :
sticky :
cookie : {}
servers :
- url : "http://192.168.1.10:9090/"
- url : "http://192.168.1.9:9191/"
在代码中,我们为已定义的guest-service loadBalancer添加了sticky属性。更改之后,请求不能再在两个应用实例之间切换。它仅由一个实例提供服务。我们可以通过在浏览器中查找 cookie 细节来验证实例细节(参见图 3-6 )。
img/497627_1_En_3_Fig6_HTML.jpg
图 3-6
浏览器 cookie
配置添加了一个具有生成名称的 cookie。sticky记录提供了以下可选属性,这些属性可以配置生成的 cookie 的行为。
Name:指定 cookie 名称,而不是生成的名称。
HttpOnly:该标志通过客户端 JavaScript 减少 cookie 访问。
安全:该属性通过 HTTPS 连接发送 cookie。
SameSite:该属性限制相同站点上下文中的 cookies。上下文边界由属性的各种值定义。
Traefik 仪表板还显示更新后的配置(见图 3-7 )。
img/497627_1_En_3_Fig7_HTML.jpg
图 3-7
粘性会话的流量仪表板
cookie 指向处理原始请求的服务器位置。必须配置 Traefik 来监控所有这些应用服务器。如果 cookie 中指定的服务器不可用,请求将被转发到新的服务器。Traefik 用新服务器的详细信息更新 cookie。这是通过为实例配置健康检查来实现的。
Note
使用经典循环路由时,Traefik 会保持不正常的服务器,直到配置了应用健康检查。
健康检查
为了有效地工作,负载平衡器应该确定哪些后端服务器运行良好。这是通过定期发送验证实例状态的请求来实现的。这些请求被称为健康检查。Traefik 仅将请求路由到健康的实例。它跟踪所有活动的实例。Traefik 在确定实例不正常时,会从活动实例池中删除该实例。它一直监视不健康的实例。实例恢复后,Traefik 会将其添加回活动实例池。只有对运行状况检查请求的响应才能控制实例的状态。除 2XX 和 3XX 之外的响应被视为错误(参见图 3-8 )。
img/497627_1_En_3_Fig8_HTML.jpg
图 3-8
应用错误
Traefik 允许您使用服务的健康检查属性来配置服务器健康。
services :
guestbook-service :
servers :
- url : "http://192.168.1.10:9090/"
- url : "http://192.168.1.11:9191/"
healthCheck:
path: /
interval: "10s"
timeout: "1s"
关于前面的代码,可以说如下。
/路径是为运行状况查找而配置的。
每 10 秒执行一次查找。如果服务器改变其状态,最多 10 秒后就会知道。
超时配置 HTTP 请求超时的时间间隔
要测试配置,您可以停止其中一个服务器,或者从应用发出错误响应(5XX,4XX)。这些运行状况检查也可以在 Traefik 仪表板的服务选项卡下看到。(参见图 3-9 )
img/497627_1_En_3_Fig9_HTML.jpg
图 3-9
具有应用运行状况的 trafik 仪表板
使用粘性会话时,如果服务器变得不可用,Traefik 会重置 cookie。然后,请求被路由到一个健康的服务器。Traefik 用新实例的详细信息更新 cookie。所有进一步的请求都被路由到新的服务器。
加权循环赛
加权循环法(WRR)考虑应用实例的资源容量(见图 3-10 )。硬件规格高于其他实例的实例可以处理更多的请求。这是通过给每个实例分配一个权重来实现的。没有确定权重的具体标准。这是留给系统管理员的。具有较高规格的节点被分配更多数量的请求。图 3-10 中的图表显示了 WRR 的请求分布。
img/497627_1_En_3_Fig10_HTML.jpg
图 3-10
请求的加权分布
到目前为止,您已经了解了权重被分配给应用的不同实例。但是在 Traefik 中,这是在服务级别而不是服务器级别表示的。在上一节中,我们配置了loadbalancer类型的服务。该服务拥有每台服务器的位置。但是要使用 WRR,我们需要将服务器逻辑上划分为不同的负载能力。这些容量服务实例中的每一个都被分组到一个具有相关权重的weighted服务实例中(参见图 3-11 )。
img/497627_1_En_3_Fig11_HTML.jpg
图 3-11
加权服务层次结构
Removed for Brevity
services :
guestbook-service :
weighted:
services:
- name: guestv1
weight: 3
- name: guestv2
weight: 1
guestv1 :
loadBalancer :
servers :
- url : "http://192.168.1.10:9090/" -- host 1
- url : "http://192.168.1.11:9191/" -- host 2
guestv2 :
loadBalancer :
servers :
- url : "http://192.168.1.12:9292/" -- Host 3
关于前面的代码,可以说如下。
上述应用有三台主机。主机 1 和主机 2 组合在一起。
guestv1已定义的分组主机配置。guestv2为主机定义配置三个实例。
guestbook-service以 3:1 的比率配置两个逻辑组。Traefik 每隔四个请求就向 h3 发送一次,而其余的请求以循环方式在 host2 和 host3 之间分配。
您可以在图 3-12 所示的 Traefik 仪表板中看到加权分布。
img/497627_1_En_3_Fig12_HTML.jpg
图 3-12
加权服务
加权服务没有运行状况检查。加权服务的健康状况取决于为底层服务配置的健康状况。
粘性会话
在上一节中,我们启用了粘性会话来将用户绑定到服务器。但是当处理加权服务时,向loadbalancer服务添加粘性属性是不够的。权重服务无法从 cookie 中识别实例详细信息。为此,我们必须在weighted服务级别配置一个 cookie。总之,会话粘性在服务层级的所有级别上都得到维护。因此,我们需要在加权服务级别和负载平衡器级别添加一个 cookie。
services :
guestbook-service :
weighted:
services:
- name: guestv1
weight: 3
- name: guestv2
weight: 1
sticky:
cookie:
httpOnly: true
guestv1 :
loadBalancer :
sticky:
cookie:
httpOnly: true
servers :
- url : "http://192.168.1.10:9090/"
- url : "http://192.168.1.9:9191/"
guestv2 :
loadBalancer :
servers :
- url : "http://192.168.1.11:9292/"
关于前面的代码,可以说如下。
它通过添加 sticky 属性为guestbook-service启用会话粘性。
它通过添加 sticky 属性为guestbookv1启用会话粘性。
配置添加了一个具有生成名称的 cookie。sticky记录具有可选属性,可以配置生成的 cookies 的行为。我们可以在浏览器 cookie 控制台中验证这些属性(参见图 3-13 )。配置的 cookie 细节也可以在 Traefik 仪表板上看到(参见图 3-14 )。
img/497627_1_En_3_Fig14_HTML.jpg
图 3-14
加权服务的粘性会话
img/497627_1_En_3_Fig13_HTML.jpg
图 3-13
所有服务的浏览器 cookies
Note
Traefik 还支持动态请求路由,其中对每个请求进行权重评估。但是该功能在 1 中可用。x 而不在 2.X 中。
反映
流量阴影或镜像是一种部署模式,其中生产流量被复制并发送到两个地方。原始的生产服务器获得实际的请求,并将其复制到测试环境中。这个过程有助于验证应用新版本中的回归问题。如果测试版本具有相同的请求 URL 和参数,那么镜像可以验证新版本是否尽可能接近无错误。
流量镜像通常是异步完成的。这确保了原始请求处理不会受到任何影响。此外,所有镜像请求都是一次性的。总之,来自镜像的响应被忽略。在任何情况下,它都不会传播回客户端。
按照惯例,我们不会将所有请求复制到镜像服务。如果这样做,它将需要一个可与生产相媲美的测试基础设施。因此,只有一定比例的请求被复制到镜像服务。Traefik 将镜像服务配置为不同类型的服务。它并不限制你只有一个镜像服务。我们可以根据需要添加任意多的镜像服务。我们只需要像在 WRR 那样建立一个服务体系。
services :
guestbook-service :
mirroring:
service: guestv1
mirrors:
- name: guestv2
percent: 10
guestv1 :
loadBalancer :
sticky:
cookie:
servers :
- url : "http://localhost:9090/"
healthCheck:
scheme : http
path: /
interval: "10s"
timeout: "1s"
guestv2 :
loadBalancer :
servers :
- url : "http://localhost:9191/"
healthCheck:
scheme : http
path: /
interval: "10s"
timeout: "1s"
在代码中,我们做了以下工作来为guest-service创建一个镜像。
顶级服务(来宾服务)被定义为由两个不同的服务组成的复合服务。
mirroring属性表明当前服务是镜像服务。我们只添加了一个镜像服务。
guestv2形容镜子。它只收到原始请求的 10%。
接下来,我们定义两个loadBalancer服务。
最后,我们为每个应用添加了healthCheck。但是服务的健康来自底层的原始生产服务。镜像的运行状况对原始服务的运行状况没有影响。配置的镜像服务也显示在 Traefik 仪表板的服务视图上(见图 3-15 )。
img/497627_1_En_3_Fig15_HTML.jpg
图 3-15
镜像服务
除了发送请求的子集,我们通常不希望发送大的请求。同样,这里的限制也是基于镜像可用的基础架构。这是通过设置 maxBodySize 属性来实现的。
services :
guestbook-service :
mirroring:
service: guestv1
maxBodySize : 1024
mirrors:
- name: guestv2
percent: 10
Removed for brevity
该法规将最大身体尺寸限定为 1024。对于镜像服务,此发送请求的大小小于 1024。
TCP 服务
Traefik 可以对 TCP 请求进行负载平衡。TCP 是许多流行应用(如 LDAP、MySQL 和 Mongo)之间的通信协议。该协议创建一个套接字连接。所有通信都是以请求-响应的方式进行的。在下一节中,我们加载 balance MongoDB 服务器。MongoDB 通过 TCP 协议进行通信。MongoDB 服务器的安装超出了本书的范围。请参考 MongoDB 文档。
在继续之前,我们需要在静态配置中为 TCP 创建一个入口点。入口点将所有传入的 TCP 请求发送到 mongo 服务器。入口点的声明方式与第二章中 HTTP 服务的声明方式相同。
entryPoints :
mongo :
address : ":80"
providers :
file :
directory : /Users/rahulsharma/traefik/ch03/code
watch : true
filename : config
api :
insecure : true
dashboard : true
一系列
我们在上一节中讨论了循环算法。该算法在列出的服务器之间平均分配请求。Traefik 允许您使用循环算法对 TCP 服务进行负载平衡。作为先决条件,您需要在两台服务器上运行 MongoDB。
tcp :
routers :
mongo-router :
entryPoints :
- mongo
rule : HostSNI(`*`)
service : mongo-tcp-service
services :
mongo-tcp-service:
loadBalancer :
servers :
- address : "192.168.1.10:27017"
- address : "192.168.1.11:27017"
关于前面的代码,可以说如下。
它描述了将请求路由到mongo-tcp-service的mongo-router。
TCP 路由器只有一个HostSNI规则。这使您能够在同一个端口上运行 TCP 和 HTTP 服务。当 TLS 支持启用时,您会看到它。
mongo-tcp-service具有与 HTTP 服务相同的声明。它由一个loadBalancer模块组成。
loadBalancer块包含一个地址列表,不像 HTTP 服务,它的位置是一个 URL。在 TCP 中,这是 IP 和端口的组合。
配置的 TCP 服务显示在 Traefik 仪表板的 TCP 服务视图中(见图 3-16 )。
以下命令使用 Mongo shell 连接到 Mongo 服务器。您可以使用db.hostInfo命令确定连接了哪台服务器。
$traefik:/# mongo -u root -p example --host localhost --port 80
MongoDB shell version v4.2.6
connecting to: mongodb:// localhost:80/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("6cc39569-0bc3-4602-b582-566afaac0382") }
MongoDB server version: 4.2.6
Server has startup warnings:
2020-05-23T13:41:25.742+0000 I STORAGE [initandlisten]
2020-05-23T13:41:25.742+0000 I STORAGE [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2020-05-23T13:41:25.742+0000 I STORAGE [initandlisten] ** See http://dochub.mongodb.org/core/prodnotes-filesystem
db.hostInfo()
img/497627_1_En_3_Fig16_HTML.jpg
图 3-16
循环 TCP 服务
终端延迟
TCP 客户端只建立一次连接。然后,它通过打开的连接发送所有请求。这与 HTTP 协议相反,在 HTTP 协议中,每个请求都在新的连接上路由。但这也带来了另一个挑战。连接可以从任一侧关闭。由于各种原因,如业务验证和服务重启,可以从服务器关闭它。它也可以从客户端关闭。由于 TCP 在双工模式下工作,每个连接关闭事件都必须得到双方的确认。如果另一端不关闭连接,连接将保持半开状态。打开的连接将锁定 Traefik 资源。
可以通过配置终止延迟来改进连接关闭处理。延迟定义了一个超时间隔,在此期间 Traefik 等待来自两端的连接关闭。延迟后,Traefik 终止连接并重新收集分配的资源。当任一方发送关闭事件时,设置延迟时钟。
removed for Brevity
services :
mongo-tcp-service:
terminationDelay: 50
loadBalancer :
servers :
- address : "192.168.1.10:27017"
- address : "192.168.1.11:27017"
延迟是在服务级别配置的。它适用于该服务下的所有服务器。延迟有一个正值,以毫秒为单位指定时间间隔。负值表示连接一直保持到双方都关闭为止。
Note
在 TCP 协议中没有状态代码或等同的代码。因此,TCP 服务没有可用的健康检查。
加权循环赛
在上一节中,我们讨论了加权循环算法。该算法允许您根据指定的权重比率分发传入的 TCP 请求。如 HTTP 服务示例所示,weighted服务是比loadBalancer服务更高级的服务。它对 TCP 服务也有相同的行为。继续上一节的 MongoDB 服务器,让我们看看加权配置。
Removed for Brevity
services :
mongo-tcp-service :
weighted:
services:
- name: mongo-1-service
weight: 3
- name: mongo-2-service
weight: 1
mongo-1-service:
terminationDelay: 42
loadBalancer :
servers :
- address : "192.168.1.10:27017"
- address : "192.168.1.11:27017"
mongo-2-service:
terminationDelay: 42
loadBalancer :
servers :
- address : "192.168.1.12:27017"
关于前面的代码,可以说如下。
上述应用有三台主机。192.168.1.10 和 192.168.1.11 组合在一起。
mongo-1-service已定义的分组主机配置。mongo-2-service为 host3 实例定义的配置。
Mongo-tcp-service以 3:1 的比率配置两个逻辑组。Traefik 每隔四个连接请求发送一次到 192.168.1.12,而其余请求以循环方式分布在 192.168.1.10 和 192.168.1.11 中。
我们可以在 Traefik 仪表板中看到加权分布。(参见图 3-17 )
img/497627_1_En_3_Fig17_HTML.jpg
图 3-17
加权循环 TCP 服务
UDP 服务
Traefik 可以对 UDP 请求进行负载平衡。UDP 是许多流行应用(如 IMAP、DNS、TFTP 和 Memcache)之间的通信协议。UDP 使用无连接的通信模型,具有最少的协议机制。没有握手,也没有保证送达。因此,该协议没有开销成本。一些应用需要这些属性,比如时间敏感的应用。
在下一节中,我们将对 TFTP 服务器进行负载平衡。TFTP 通过 UDP 协议进行通信。TFTP 服务器的安装超出了本书的范围。有关更多信息,请参考 TFTP/Unix 文档。
在我们继续之前,我们需要在静态配置中为 UDP 创建一个entrypoint,。入口点将所有传入的 UDP 流量发送到 TFTP 服务器。入口点的声明方式与第二章的中 HTTP 的声明方式相同。
entryPoints :
tftp :
address : ":69/udp"
providers :
file :
directory : /Users/rahulsharma/Projects/traefik-book/ch03/code
watch : true
filename : config
api :
insecure : true
dashboard : true
一系列
我们在前面的章节中讨论了循环算法。该算法在列出的服务器之间平均分配请求。Traefik 允许您使用循环算法对 UDP 服务进行负载平衡。作为先决条件,您需要在两台服务器上运行 TFTP。
udp :
routers :
tftp-router :
entryPoints :
- tftp
service : tftp-service
services:
tftp-service:
loadBalancer :
servers :
- address : "192.168.1.10:69"
- address : "192.168.1.11:69"
关于前面的代码,可以说如下。
我们描述了tftp-router将请求路由到tftp-service。
UDP 路由器没有任何规则。它不能执行主机名查找。它只能使用端口来执行。
tftp-service具有与 HTTP 服务相同的声明。它由一个loadBalancer模块组成。
loadBalancer块包含一个地址列表,不像 HTTP 服务,它的位置是一个 URL。在 UDP 中,它是 IP 和端口的组合。
Traefik dashboard 还提供 UDP 服务视图来显示已配置的 UDP 服务。(参见图 3-18 )
img/497627_1_En_3_Fig18_HTML.jpg
图 3-18
循环中的 UDP 服务
Note
UDP 服务没有可用的运行状况检查。
以下命令通过使用tftp命令连接到 TFTP 服务器。您可以传输我们在服务器上提供的示例文件。
traefik $ tftp 192.168.1.4
tftp> verbose
Verbose mode on.
tftp> get sample.md
getting from 192.168.1.4:sample.md to sample.md [netascii]
Received 682 bytes in 0.0 seconds [inf bits/sec]
加权循环赛
前面几节讨论了加权循环算法。该算法允许您根据规定的权重比率分发传入的 UDP 消息。如 HTTP 服务示例所示,weighted服务是比loadBalancer服务更高级的服务。它对 UDP 服务也有相同的行为。继续上一节的 TFTP 服务器,让我们看看加权配置。
关于前面的代码,可以说如下。
上述应用有三台主机。192.168.1.10 和 192.168.1.11 组合在一起。
tftp-1-service定义分组主机的配置。tftp-2-service定义 host3 实例的配置。
tftp-service以 3:1 的比率配置两个逻辑组。Traefik 每隔四个连接请求发送一次到 192.168.1.12,而其余请求以循环方式分布在 192.168.1.10 和 192.168.1.11 中。
我们可以在 Traefik 仪表板中看到加权分布。(参见图 3-19 )
img/497627_1_En_3_Fig19_HTML.jpg
图 3-19
加权 UDP 服务
摘要
在本章中,您看到了 Traefik 中不同的负载平衡功能。我们为 HTTP、TCP 和 UDP 通信配置了经典循环法和加权循环法。您还处理了 HTTP 通信的 stick 会话和健康检查。最后,您看到了用于 canary 部署的镜像功能。在下一章中,您将看到 Traefik 中可用的 TLS 功能。
四、配置 TLS
前几章介绍了如何通过 HTTP、TCP 和 UDP 连接使用 Traefik 公开服务。他们还深入了解了 Traefik 提供的特殊交通管理功能。到目前为止,您只处理过普通的未加密流量——HTTP 或 TCP。但是,对于任何严肃的生产用途,您都需要通过 TLS 安全地公开端点。在本章中,您将了解 Traefik 为加密和解密网络流量提供的功能。
涵盖了以下两种情况。
带 trafik 的 tls 终端
TLS 转发到后端服务
对于 Traefik 的 TLS 终止,我们使用 Let's Encrypt 为公共云中运行的服务自动提供 TLS 证书。Traefik 和 Let's Encrypt 一起让这个复杂的过程变得相当琐碎。
TLS 快速概述
TLS 加密的常见用例是保护 HTTP 流量。这意味着所有 API 和 web 流量都可以抵御中间人攻击和其他形式的网络窥探。我们不是公开普通的 HTTP 流量,而是通过 HTTPS 路由数据包。这意味着传输数据包的底层通道是加密的。
这种安全性的主要需求是保护敏感数据,从您可以用来验证对受保护资源的访问的用户名/密码开始。你不应该在普通的 HTTP 连接上传输任何秘密,因为它很容易被网络嗅探软件截获。
TLS 流量在离开源端时被加密,然后在目的端被解密。解密部分就是我们所说的 TLS 终止。这种加密和解密通常在应用层(OSI 或 TCP/IP 术语)之下执行。
除了指定适当的配置之外,大多数应用层代码从不需要担心 TLS 的细节。因为实现加密协议不适合胆小的人,也不应该掉以轻心,所以大多数编程语言和平台都带有标准库,这些库已经过实战测试,并由密码学专家实现。
Note
尽管我们触及了在 Traefik 中使用 TLS 的许多部分,但对 TLS 的深入讨论超出了本书的范围。它的核心是基于标准的密码原语,特别是公钥密码学,这是一个专门的领域。本演练涵盖使用 TLS 的实际方面,包括从证书颁发机构获取有效的 TLS 证书,并在客户端-服务器配置中使用它们。我们不涉及高级网络安全实践或 DNS,尽管我们确实对它们有所涉猎。我们鼓励您更深入地研究这些领域,根据它们的用例来微调配置。
一般来说,连接到启用了 TLS 的服务器的客户端可以验证其真实性。它们通过向证书颁发机构(或 CA)验证服务器的 TLS 证书来实现这一点,证书颁发机构签署并颁发服务器的证书。该服务器证书包含服务器的公钥,该公钥在客户端和服务器之间建立加密通道。这有助于防范中间人和其他攻击。CA 通常是受信任的第三方公共机构,大多数浏览器已经自动配置了这些。大型专用企业网络通常有一个专用 CA 来为内部 Intranet 站点颁发证书,并且该专用 CA 在企业管理的设备上自动受到信任。
这种机制有一个扩展,可以向服务器验证客户端的身份;这是通过通常由同一个 CA 颁发的客户端证书来实现的,并在服务器端进行验证。虽然 Traefik 支持这种方法,但我们并不在本章中讨论这种方法。
通常,HTTP 流量的默认端口是 80,HTTPS 是 443,尽管各种平台和应用在它们选择的任何端口上提供 TCP 和 HTTP/HTTPS 流量。例如,端口 8080 和 8443 分别是公开 HTTP 和 HTTPS 流量的非常流行的备选方案。(它们是非限制性端口,但是这方面的讨论超出了本书的范围。)然而,大多数面向互联网的服务器都公开标准端口 80 和 443。到目前为止,我们主要公开了 8080、80 或其他 HTTP 和 TCP 自定义端口上的 Traefik 入口点。Traefik 仪表板通过 HTTP 8080 或 80 以未经身份验证的不安全模式公开,不建议在生产环境中使用。
虽然 TLS 广泛保护 web 上的 HTTP 流量,但我们使用它来保护普通的 TCP 连接。我们可以很容易地将 HTTPS 暴露给前几章中介绍的一个 HTTP APIs 示例。我们认为 TLS 比简单 TCP 更好地展示了 Traefik 的高级 TLS 功能。作为通过 TLS 公开 TCP 端口的一部分,我们还包括一些 HTTPS 流量。
在 Traefik 终止 TLS
前一章介绍了如何使用 Traefik 公开和负载平衡 MongoDB TCP 服务。本地使用有一个简单的连接是好的;然而,对于任何一种严肃的生产用例,您都需要使用 TLS 加密到 MongoDB 的连接。您可以通过两种方式实现这一点。
在 MongoDB 服务器上启用 TLS,这样 TLS 在 MongoDB 服务器级别终止。这要求 Traefik 传递加密的流量。或者,
MongoDB 仍然服务于普通的 TCP 流量;但是,我们在 Traefik 入口点上启用 TLS 来公开 MongoDB 连接。然后,TLS 在 Traefik 入口点终止,该入口点将解密的流量传递到后端 MongoDB 端口。
虽然第一种选择似乎更有利和安全,但这在实践中通常不是可行的方法。由于 Traefik 通常充当负载平衡器或反向代理,因此它通常会在多个后端实例之间对您的请求进行负载平衡。这在上一章中已经讲过了。
通常,您有多个集群节点或服务实例(分别用于数据库和 API)暴露在一个路由后面。在这种情况下,任何客户端都希望将一个一致的端点暴露给它们所连接的任何服务。出于安全原因,TLS 证书与部署它们的服务器域主机紧密耦合。如果我们在单个实例级别终止 TLS,那么我们需要额外的证书,或者在每个复制副本实例上重复使用相同的证书。对于消费者来说,很明显客户端每次都连接到不同的主机。这是不太可取的。
在实际生产中,我们总是使用 Traefik 进行 TLS 终止,然后通过简单的 TCP 连接将数据包从 Traefik 路由到后端(在当前示例中是 MongoDB)。这并没有看起来那么大的安全风险,因为内部流量是在一个封闭的网络中,通常是您自己的 VPC/VLAN。对于共享基础设施的场景,总是有一种高级的做法,即启动一个从边缘网关到后端服务的新的内部 TLS 连接;我们不报道这个。
在 TLS 上公开 MongoDB 路由
本节通过公共云提供商在云 VM 上运行 MongoDB 实例。您可以使用托管数据库产品。如果您调整了正确的端口并为该数据库使用了合适的客户机,那么您可以自由地使用任何其他数据库。为了简单起见,我们在同一个云 VM 上运行 MongoDB 实例和 Traefik,这样 Traefik 可以安全地连接到在本地主机上运行的 MongoDB,而没有任何其他复杂性。
对于这个例子,我们正在运行一个运行 Ubuntu 18 的 DigitalOcean droplet(或 VM)。然而,这些概念对于任何其他云 VM 都是相同的,比如 AWS EC2 实例。设置 DigitalOcean 或 AWS 实例超出了本书的范围;然而,这很容易做到。一旦 VM 设置完成,Traefik 可以使用第一章中的说明轻松安装。
默认情况下,不允许 Ubuntu Traefik CLI 监听端口 80 或 443。因此,如果这些端口被定义为入口点,Traefik 会在启动时抛出一个错误。我们需要运行清单 4-1 中的命令来允许 Traefik 绑定到这些端口。
ubuntu-blr1-01:~$ wget https://github.com/containous/traefik/releases/download/v2.2.1/traefik_v2.2.1_linux_amd64.tar.gz
ubuntu-blr1-01:~$ tar -zxvf traefik_v2.2.1_linux_amd64.tar.gz
ubuntu-blr1-01:~$ sudo setcap 'cap_net_bind_service=+ep' traefik
ubuntu-blr1-01:~$ ./traefik --entryPoints.web.address=:80
Listing 4-1Install and Set Permissions for Traefik
您可能想知道我们为什么选择云部署。原因是我们需要一个公共 DNS 名称,Traefik 才能获得有效的 TLS 证书。我们建立了一个单独的子域来指向 DigitalOcean droplet,并在 DNS 提供程序中添加了一个 A 记录来指向它的 IP 地址(同样,这个讨论也超出了本书的范围)。您可以在本地尝试 Traefik 的 TLS 支持,但您需要手动提供或自签名的证书。你也会错过 Traefik 让整个过程变得多么简单。手动获取的证书的 TLS 配置很简单;我们在此提供它以供参考。这对于有效的公共域证书和本地自签名测试证书都是一样的。
清单 4-2 用一个tls: {}键定义了一个 TCP 路由。这足以在此路由上启用 TLS。
traefik.yml with only 443 entrypoint
entryPoints:
websecure:
address: ":443"
Also enable DEBUG log
log:
level: DEBUG
providers:
file:
filename: "traefik-tls-conf.yml"
watch: true
traefik-tls-conf.yml
tcp :
routers :
mongo-router :
entryPoints :
- websecure
rule : "HostSNI(`localhost`) || HostSNI(`127.0.0.1`)"
service : mongo-tcp-service
tls: {} #This block will enable TLS for this route
We also need to provide the TLS certificates
tls:
certificates:
- certFile: localhost+1.pem
keyFile: localhost+1-key.pem
Listing 4-2Route Configuration for MongoDB TCP over TLS: Manual Certificates
我们还在单独的部分提供了证书路径。这是一个动态配置,这意味着我们可以在运行时添加新的证书。这些只能通过 FileProvider 定义。根据匹配的域,在运行时使用正确的证书。如果您没有提供自己的证书,或者没有为匹配的域提供证书,Traefik 会生成并使用其默认的自签名证书。也可以覆盖这个默认证书。手动生成证书超出了本章的范围。对于本地测试,像 https://mkcert.dev/ 这样的工具会很有用。在前面的配置中,您可以通过安全的 TCP+TLS 连接连接到 MongoDB。
MongoDB 的安装在书外;然而,它基本上是默认安装,只是增加了一点——为了基本的安全性,我们设置了一个 DB 用户和密码。为了简单起见,我们只使用一个后端 MongoDB 实例,而不是跨多个实例进行负载平衡。配置看起来如清单 4-3 所示。
tls-config.yaml dynamic config
tcp :
routers :
mongo-router :
entryPoints :
- mongo
rule : "HostSNI(`tlstraefik.rahulsharma.page`)"
service : mongo-tcp-service
tls:
certResolver: "letsencrypt"
domains:
- main: "tlstraefik.rahulsharma.page"
services :
mongo-tcp-service :
loadBalancer :
servers :
- address : "localhost:27017"
Listing 4-3Route Configuration for MongoDB TCP over TLS
这些作品中的大部分已经为我们所知。我们查看了HostSNI属性。在前面的例子中,我们将其设置为匹配所有可能的主机名(*)。虽然这种方法对于未加密的流量可能没问题,但对于 TLS 是不可行的。它与运行服务的实际域名相匹配,并且服务器证书也验证了这一点。tlstraefik.rahulsharma.page是指向 DigitalOcean droplet 的 IP 地址的公共 DNS 名称。还有一个设置为letsencrypt ,的certresolver属性,它自动提供一个 TLS 证书,从第一次请求时加密到这个端点。
让我们加密自动证书供应
获取 TLS 证书通常是一个多步骤的过程。所有平台上都有很好的实用程序,可以帮助你生成必要的片段。对于无处不在的 TLS 证书,证书生成是一个复杂的过程,需要设置许多属性,如果某些配置是错误的,您需要重新开始。通常的步骤是为服务器证书生成证书签名请求(CSR ),并将其提交给 CA 进行签名。CA 签署并返回可以使用的有效证书。不同的 ca 有不同的定价模式,证书功能和网站可以自由选择他们想要的。传统上,这是一个手动过程,其中包含一些自动化部分。
这个领域的大颠覆是由一个叫做“让我们加密”( https://letsencrypt.org )的非营利组织带来的。Let's Encrypt 已经为超过 2.25 亿个网站颁发了免费的 TLS 证书(超过 10 亿份)。它通过编程 API 自动完成整个过程,因此不需要人工干预。
让我们加密几个官方和第三方客户端,使最终用户的证书提供过程变得简单。它通过利用 ACME 标准( https://tools.ietf.org/html/rfc8555 )来实现这种自动化,ACME 标准代表自动证书管理环境。我们不会深入研究这个问题,但是它完全自动化了从 ACME 兼容的 CA 获取和管理证书的工作。最重要的是,你必须做的就是启动你的 HTTPS 服务器,剩下的就交给你了。如果有人正在考虑建立一个网站,需要 TLS 证书,让我们加密是正确的做法。
Note
让我们只加密面向 Internet 的公共站点的设置证书。这意味着您的端点需要可以通过公共域名访问,以便我们加密来提供 TLS 证书。对于私有或内部 API,需要从其他 ca 提供证书。这对示例有影响,到目前为止,这些示例一直在本地系统上运行。
为公共 TCP 端点设置 TLS 证书
为了自动提供来自 Let's Encrypt 的 TLS 证书,我们需要在公共互联网上公开 MongoDB 端口。
显然,作为一种实践,您永远不应该在互联网上公开您的数据库端口。有许多新闻报道称,不安全的 MongoDB 连接暴露在开放的互联网上,并成为黑客的目标,导致数据受损。对于这个例子,我们暂时使用一个空白数据库,所以应该没问题。我们还使用适当的访问凭证来保护 MongoDB,该端口只能通过 Traefik 访问。这里需要注意的一点是,我们还在虚拟机上设置了云防火墙规则,只允许端口 80、443 和 4445 上的入站流量。来自 Internet 的所有其他入站流量都被阻止。
如清单 4-2 所示,我们在tlstraefik.rahulsharma.page域上公开了 Mongo TCP 端点。tlstraefik这里是 TLS 路由匹配的子域。Let's Encrypt 颁发的证书是为同一个子域颁发的。支持额外的域条目,这些条目请求额外的 SNI(服务器名称指示)主机名,这些主机名支持同一 IP 和端口上的多个域和 TLS 证书。我们不打算深究此事。
Traefik 首先检查域条目,然后检查 HostSNI 值,以确定自动请求 TLS 证书的域。这里两者都不需要;我们把它们包括进来只是为了参考。这对于通配符证书更有用,这里不讨论。
引用了certresolver属性。它与入口点一起在静态配置中定义。我们需要定义一个 TLS 入口点和证书解析器,目前主要是加密。
在清单 4-4 中,有两个感兴趣的条目。首先是要指定的caServer URL 属性。通常,默认情况下,这是让我们加密生产 API 端点。然而,这个终点有严格的(尽管是宽松的)速率限制。
traefik.yaml static config
entryPoints:
mongo:
address: ':4445'
https:
address: ':443'
providers:
file:
watch: true
filename: tls-config.yml
certificatesResolvers:
letsencrypt:
# ACME support via Let's Encrypt
acme:
# Email address required for certificate registration
email: "<email address>"
# File or key required for certificates storage.
storage: "acme.json"
# CA server URL
caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"
tlsChallenge: {}
Listing 4-4Entrypoint and Certificate Resolver
如果您是第一次使用 Let's Encrypt 并在测试环境中试验配置来弄清楚您的 TLS 设置,那么使用 Let's Encrypt staging 端点的 URL 更有意义,它在清单 4-4 中使用。这不提供可用的 TLS 证书。签署证书的 CA 是假的,大多数浏览器拒绝由此产生的证书。Let's Encrypt 允许您下载虚拟 CA 证书(fakelerootx1.pem ),并根据它验证您生成的服务器证书。这允许您在使用有效证书进入生产环境之前测试整体集成。
其次,tlsChallenge属性是我们感兴趣的。为了验证请求服务器证书的第三方拥有主机的所有权,让我们加密支持各种自动化挑战。标准的挑战是 HTTP-01 挑战和 DNS-01 挑战。两者都适合不同类型的用例。然而,TLS 终止反向代理有一个特殊的挑战,这就是 Traefik 在这里扮演的角色,称为 TLS-ALPN-01 挑战。更多的信息超出了本书的范围。在 Traefik 中配置这种质询类型很简单,所以在这里使用它。
有一个警告:这个挑战需要 Traefik 访问端口 443;因此,我们在端口 443 上暴露了一个额外的入口点。这没什么大不了的,因为我们无论如何都会在下一节中使用这个端口。对于基于云的安装,有必要在入站流量防火墙规则中打开端口 443。当您在 Traefik 仪表板上查看时,您会看到两个入口点(参见图 4-1 )。
img/497627_1_En_4_Fig1_HTML.jpg
图 4-1
TLS entry points-TLS 输入类型
如果您使用这个 Traefik 配置来公开与 Traefik 实例运行在同一服务器上的 MongoDB 实例,您可以在主机名 tlstraefik.rahulsharma.page 、上访问端口 4445 上的 MongoDB,但只能使用 TLS 连接。您可以用 Mongo 客户端 CLI 来尝试一下(参见清单 4-5 )。
ch04 % mongo --port 4445 --host tlstraefik.rahulsharma.page
MongoDB shell version v4.2.7
connecting to: mongodb:// tlstraefik.rahulsharma.page:4445/?compressors=disabled&gssapiServiceName=mongodb
Listing 4-5Mongo Client Simple Connection
这并不连接到 Mongo 实例,也不工作。为了让它工作,我们需要传递一个--tls选项(参见清单 4-6 )。
ch04 % mongo --port 4445 --host tlstraefik.rahulsharma.page --tls
MongoDB shell version v4.2.7
connecting to: mongodb:// tlstraefik.rahulsharma.page:4445/?compressors=disabled&gssapiServiceName=mongodb
2020-07-05T17:44:26.901+0530 E NETWORK [js] SSL peer certificate validation failed: Certificate trust failure: CSSMERR_TP_NOT_TRUSTED; connection rejected
2020-07-05T17:44:26.901+0530 E QUERY [js] Error: couldn't connect to server tlstraefik.rahulsharma.page:4445, connection attempt failed: SSLHandshakeFailed: SSL peer certificate validation failed: Certificate trust failure: CSSMERR_TP_NOT_TRUSTED; connection rejected :
connect@src/mongo/shell/mongo.js:341:17
@(connect):2:6
2020-07-05T17:44:26.904+0530 F - [main] exception: connect failed
2020-07-05T17:44:26.904+0530 E - [main] exiting with code 1
Listing 4-6Mongo Client TLS Connection
这也不行;如果您使用了“让我们加密”的临时 CA URL,则无法验证生成的服务器证书。要用这个证书连接到 MongoDB,您需要传递一个–tlsAllowInvalidCertificates标志或--tlsCAFile选项(参见清单 4-7 )。
ch04 % mongo --port 4445 --host tlstraefik.rahulsharma.page –tls --tlsCAFile fakelerootx1.pem
MongoDB shell version v4.2.7
connecting to: mongodb:// tlstraefik.rahulsharma.page:4445/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("0b2ccc38-f8bd-4346-a08a-3da0ef9793b0") }
MongoDB server version: 3.6.3
use admin
switched to db admin
db.auth("akshay", "password");
1
db.version()
3.6.3
Listing 4-7Mongo Client TLS Connection
如果您简单地省略清单 4-4 中的caServer URL 属性,Traefik 会自动连接到 Let's Encrypt production URL 并获取一个有效的证书。然后,清单 4-8 中的命令开始工作。
ch04 % mongo --port 4445 --host tlstraefik.rahulsharma.page –tls
MongoDB shell version v4.2.7
connecting to: mongodb:// tlstraefik.rahulsharma.page:4445/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("0b2ccc38-f8bd-4346-a08a-3da0ef9793b0") }
MongoDB server version: 3.6.3
>
Listing 4-8Mongo Client Valid TLS Certificate
这里有两点需要注意。首先,默认情况下,Traefik 将获得的证书保存在一个名为 acme.json 的文件中。您可以在清单 4-4 中看到这个文件名。如果需要,我们可以添加自定义文件名或位置。如果从临时 URL 切换到生产 URL,您还需要删除此文件或使用其他位置。否则,Traefik 默认使用已经保存的证书。
其次,虽然 TLS 终端可能与入口点端口(本例中为 4445)紧密相关,但 TLS 配置是由动态路由器配置驱动的。归根结底,Traefik 不会向 Let's Encrypt 发送证书生成请求,直到它为特定的路由进行了配置。因此,为同一个入口点生成不同的证书来服务不同的路由是完全可行的。
我们可以在 Traefik 仪表板中看到下面的 TCP 路由器,其中提到了 TLS 细节(参见图 4-2 和图 4-3 )。
img/497627_1_En_4_Fig3_HTML.jpg
图 4-3
带有 TLS 详细信息的 TCP 路由器
img/497627_1_En_4_Fig2_HTML.jpg
图 4-2
启用 TLS 的 TCP 路由器
通过 tls 保护流量仪表板
到目前为止,我们已经在不安全的模式下公开了 Traefik 仪表板,以检查 UI 上的配置。但是,在公共云上,您希望控制面板受到身份认证和 TLS 的保护。作为其中的一部分,我们在同一台主机上的 entrypoint 443 上公开 Traefik 仪表板,并在显式仪表板路由上应用身份验证中间件。该路由在同一个主机名上公开,尽管我们可以为带有 DNS 条目和 TLS 证书的仪表板指定不同的主机名。这方面的配置见清单 4-9 。我们还将端口 80 上的所有 HTTP 流量重定向到端口 443 上的 HTTPS。
traefik.yaml static config
entryPoints:
https:
address: ':443'
http:
address: :80
http:
redirections:
entryPoint:
to: https
scheme: https
providers:
file:
watch: true
filename: tls-config.yml
api:
dashboard: true
tls-config.yaml dynamic config
http:
routers:
dashboard:
entryPoints:
- https
rule: "Host(`tlstraefik.rahulsharma.page`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
service: api@internal
tls:{}
middlewares:
- auth
middlewares:
auth:
basicAuth:
users:
- "admin:$apr1$JsindKAS$zCWAvabJOgQvI.Dd3zjtE."
Listing 4-9Entrypoint and Route Config for Secure Dashboard
在应用这种配置时,您可能会观察到两种不同的结果。如果您已经执行了前面几节中为 MongoDB 配置 TLS 的步骤,那么 Traefik 将重用服务器为 MongoDB 路由获得的相同证书,因为它们共享相同的主机条目。但是,如果忽略 MongoDB TLS 配置,Traefik 会返回生成一个默认的自签名 TLS 证书。您在浏览器中观察到类似图 4-4 所示的情况,它抱怨证书问题,并且不让您继续。这是因为我们没有为仪表板路线指定任何certResolver值。
img/497627_1_En_4_Fig4_HTML.jpg
图 4-4
自签名 Traefik 证书错误
如果我们检查证书,我们可以看到细节(见图 4-5 )。
img/497627_1_En_4_Fig5_HTML.jpg
图 4-5
自签名 Traefik 证书详细信息
您也可以为仪表板路线使用一个certResolver属性。在配置中甚至可以有两个certResolver属性:一个指向让我们加密暂存,另一个指向生产。在清单 4-10 中可以看到一个例子,我们为仪表板定义了一个不同的域(它仍然必须是一个有效的公共域)和一个 staging certResolver。
traefik.yaml has both staging and prod LE config
only relevant config for brevity
certificatesResolvers:
letsencrypt:
acme:
email: ""
storage: "acme.json"
tlsChallenge: {}
letsencrypt-staging:
acme:
email: ""
storage: "acme-staging.json"
# CA server to use.
caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"
tlsChallenge: {}
tls-config.yaml dynamic config
http:
routers:
dashboard:
entryPoints:
- https
rule: "Host(`dashboard.rahulsharma.page`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
service: api@internal
tls:
certResolver: "letsencrypt-staging"
Rest of config omitted for brevity
Listing 4-10Multiple Lets Encrypt Resolvers
在这种配置下,您会在浏览器中看到以下错误(参见图 4-6 ),并且不允许您继续操作。
img/497627_1_En_4_Fig6_HTML.jpg
图 4-6
让我们加密临时证书的详细信息
一旦我们修复了配置并从 Let's Encrypt 获得了正确的生产证书,我们就可以在浏览器中检查证书的详细信息(参见图 4-7 )。我们还被要求提供仪表板的基本认证凭证(见图 4-8 )。
img/497627_1_En_4_Fig8_HTML.jpg
图 4-8
仪表板的基本身份验证
img/497627_1_En_4_Fig7_HTML.jpg
图 4-7
让我们加密有效的证书细节
tls 转发的流量
在某些罕见的情况下,您的目标服务或数据库可能需要自己管理 TLS 终止,而不需要在其间使用反向代理。这意味着 Traefik 需要在不解密或终止的情况下转发 TLS 流量。令人高兴的是,Traefik 通过passthrough选项很容易支持这一点。我们需要在启用服务器 TLS 的情况下运行 MongoDB,并使用有效的 TLS 证书来证明这种支持。配置超出了本书的范围;然而,通过遵循 MongoDB 文档,这应该很容易做到。MongoDB 配置可能会看起来像清单 4-11 所示。
net:
port: 27017
bindIp: 127.0.0.1,tlstraefik.rahulsharma.page
tls:
mode: requireTLS
certificateKeyFile: /etc/ssl/mongodb.pem
Listing 4-11Sample MongoDB TLS Configuration in /etc/mongod.conf
和以前一样,您可以自由选择任何其他目标,例如另一个云管理的数据库,连接的过程是不同的。您也可以在本地尝试这种方法;您需要自签名服务器证书和您自己的 CA 证书。对此的深入讨论超出了本节的范围;然而,OpenSSL 是一个很好的实用程序,可以在所有主流平台上生成 TLS 证书。
本节的 Traefik 设置与之前运行在同一云 VM 主机上的 Traefik 实例相同,防火墙上暴露了必要的 Traefik 端口。然而,这次我们没有为mongo-router路由定义一个certResolver,而是添加了一个不同的属性,如清单 4-12 所示。
tls-config.yaml dynamic config
tcp :
routers :
mongo-router :
entryPoints :
- mongo
rule : "HostSNI(`tlstraefik.rahulsharma.page`)"
service : mongo-tcp-service
tls:
passthrough: true
Rest omitted for brevity
Listing 4-12Route Configuration for TLS Forwarding
当我们试图在这个主机/端口上访问 MongoDB 时,Traefik 会将 TLS 连接不加解密地转发到运行在同一个 VM 上的 MongoDB 服务器,在那里发生实际的 TLS 终止,如清单 4-13 所示。
code % mongo --tls --host tlstraefik.rahulsharma.page --port 4445
MongoDB shell version v4.2.8
connecting to: mongodb://tlstraefik.rahulsharma.page:4445/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("88df0a97-6ff8-4764-897b-82746b621598") }
MongoDB server version: 4.2.8
use admin
switched to db admin
db.auth("akshay", "password");
1
Listing 4-13Connect Mongo over TLS
如果您在 Traefik 仪表板上检查此配置,您会看到 passthrough 属性设置为 true(参见图 4-9 )。
img/497627_1_En_4_Fig9_HTML.jpg
图 4-9
带 tls 穿透的 trafik 路由器
对于客户端应用,行为没有真正的改变。至此,我们结束了这一节和这一章。为了进一步深入,我们鼓励您深入研究 Traefik 文档中提供的不同 TLS 配置选项。
摘要
在这一章中,您快速地、表面地了解了设置 TLS 证书来保护您的网络流量的深层领域。我们理解,你们中的一些人可能会对我们快速浏览的所有主题感到不知所措。我们仅仅触及了 TLS 证书生态系统的表面。然而,我们认为我们已经展示了 Traefik 与 Let's Encrypt 结合使用是多么容易,特别是对于公共 DNS 域。事实上,您可能会发现,从 Let's Encrypt 为 Traefik 端点提供有效的 TLS 证书比生成自签名或有效的 TLS 证书更容易。如果你对 TLS 更感兴趣,我们鼓励你去探索它。
为了安全起见,我们没有提到通过 Let's Encrypt 获得的证书每 90 天更新一次。在传统的 IT 环境中,证书续订是一个繁琐的手动过程,必须由人来跟踪。因此,随着长期运行的 TLS 证书的使用,这样做的频率降低了。有了 Traefik/Let's Encrypt 集成,这是自动处理的,不需要任何手动干预。Let's Encrypt 会在您注册时使用的电子邮件地址上自动发送提前提醒。
在下一章中,您将深入了解 Traefik 中针对操作问题提供的大量简单选项。Traefik 使收集运行时指标变得非常容易,并与许多标准监控框架集成。
五、日志、请求跟踪和指标
业务运营执行应用监控。该流程旨在发现并修复应用停机,以免影响正常业务运营。传统上,团队执行简单的检查,如流程启动/关闭或端口打开/关闭。但是这些支票还不够好。随着时间的推移,已经开发了许多工具来改进应用监控过程。该过程包括捕获使用指标和执行分析。但是,仅仅依靠应用监控是一种软弱的做法。应用监控只能提供关于持续应用问题的通知。下一步是确定根本原因。
根本原因主要与上下文有关:一个新特性出现故障,或者规范中遗漏了一些控件,或者用户正在执行一个有效的请求,导致“内存不足”,等等。我们无法仅通过查看通知得出结论。我们需要更多的信息来确定根本原因。这就是所谓的失败背景。
上下文是通过首先查看应用日志(如果有)来创建的。堆栈跟踪提供了可能的错误的线索,但是错误是由特定的边缘场景引起的。这些边缘场景由用户数据和应用状态定义。如果已经捕获了用户数据,则从请求访问日志中确定用户数据。所有这些说起来容易做起来难。
多年来,企业应用环境变得越来越复杂。以前的实践不足以处理停机。Google 推出了请求追踪的做法。请求跟踪捕获了跨不同分布式系统的用户请求流。这个互补的过程有助于预测失败的场景和涉及的系统。
总之,日志、度量和跟踪是互补的实践(见图 5-1 )用于不同的目的。在大修期间,这些实践中没有一个是单独足够的。因此,应用监控的简单实践已经从单个应用状态转变为整个生态系统的整体视图。这也被称为可观测性。可观察性包括度量、日志和跟踪的收集、可视化和分析,以获得对系统运行的整体理解。
img/497627_1_En_5_Fig1_HTML.jpg
图 5-1
可观察性数据
像 Twitter、Google、优步等公司,开创了可观察性,定义了基于以下支柱的完整实践。
应用和业务指标
日志
分布式跟踪
警报和通知
形象化
Note
与监控相比,可观察性项目为什么有问题,监控只是告诉什么时候有问题。
作为 API 网关,Traefik 是所有外部发起的用户请求的单一入口点。它必须与企业现有的解决方案集成,以捕获所有的请求流和指标。为了捕获端到端的请求流,Traefik 需要生成请求范围,并将其发送给跟踪后端系统。Traefik 还需要生成访问日志和基于请求的度量,以建立对分布式系统行为的可见性。本章通过一个示例 HTTP 应用来讨论这些特性。
先决条件
在本章中,我们使用一个 HTTP 服务的例子。我们部署和配置 httpbin 服务( https://github.com/postmanlabs/httpbin )来服务我们的目的。这是一个开源应用。服务是用 Python 写的。我们需要一个 Python 运行时来运行应用。部署的服务是使用 Traefik 配置的。
Note
这是一个可选步骤。这是一个用于验证配置更改的示例服务。如果您有正在运行的服务,可以跳过这一步。
首先,检查所需的python, pip,和virtualenv命令。
~/Projects$ python3 --version
Python 3.8.0
~/Projects$ pip3 --version
pip 19.2.3 from /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/pip (python 3.8)
~/Projects$ virtualenv --version
16.7.8
确保您拥有 Python 和 pip 的 3.x 版本。如果命令失败,您需要安装所需的软件。Python、pip 和 virtualenv 的安装说明超出了本书的范围。
下一步,我们从发布页面 https://github.com/postmanlabs/httpbin/releases 下载一个版本的 httpbin 服务(见图 5-2 )。在撰写本文时,0.6.1 是最新的发布版本。
img/497627_1_En_5_Fig2_HTML.jpg
图 5-2
httpbin 版本
下载发布的工件,并将它们解压缩到一个目录中。该目录包含源文件、应用许可证、构建文件等等。目的是编译代码并从中获得一个二进制工件。
~/Projects/httpbin-0.6.1$ ls -1
AUTHORS
Dockerfile
LICENSE
MANIFEST.in
Pipfile
Pipfile.lock
Procfile
README.md
app.json
build
dist
httpbin
httpbin.egg-info
setup.cfg
setup.py
test_httpbin.py
tox.ini
该服务是使用setuptools构建的。您可以部署和运行服务,如下所述。
创建一个虚拟环境,然后激活它。
以develop模式构建服务。
(venv) ~/Projects/httpbin-0.6.1$ python setup.py develop
running develop
running egg_info
writing httpbin.egg-info/PKG-INFO
#
removed for brevity
#
/Users/rahulsharma/Projects/httpbin-0.6.1/venv/bin
Using /Users/rahulsharma/Projects/httpbin-0.6.1/venv/lib/python3.8/site-packages
Finished processing dependencies for httpbin==0.6.1
(venv) ~/Projects/httpbin-0.6.1$
在 Gunicorn 中部署应用.
~/Projects/httpbin-0.6.1$ virtualenv venv
Using base prefix '/Library/Frameworks/Python.framework/Versions/3.8'
New python executable in /Users/rahulsharma/Projects/httpbin-0.6.1/venv/bin/python3.8
Also creating executable in /Users/rahulsharma/Projects/httpbin-0.6.1/venv/bin/python
Installing setuptools, pip, wheel...
done.
~/Projects/httpbin-0.6.1$ source venv/bin/activate
(venv) ~/Projects/httpbin-0.6.1$
(venv) ~/Projects/httpbin-0.6.1$ gunicorn -b 0.0.0.0 httpbin:app
[2020-06-12 14:35:04 +0530] [67528] [INFO] Starting gunicorn 20.0.4
[2020-06-12 14:35:04 +0530] [67528] [INFO] Listening at: http://0.0.0.0:8000 (67528)
[2020-06-12 14:35:04 +0530] [67528] [INFO] Using worker: sync
[2020-06-12 14:35:04 +0530] [67530] [INFO] Booting worker with pid: 67530
httpbin 服务现在正在我们的系统上运行。您可以在http://localhost:8000访问它(参见图 5-3 )。您还可以测试一些可用的端点。
img/497627_1_En_5_Fig3_HTML.jpg
图 5-3
httpbin 服务
流量配置
在上一节中,我们添加了一个 HTTP 服务。现在让我们配置 Traefik 向它发送用户请求。我们将为 web 应用创建以下带有入口点的treafik.yml。
entryPoints :
web :
address : ":80"
providers :
directory : /Users/rahulsharma/Projects/traefik-book/ch05/services
watch : true
filename : config
debugLogGeneratedTemplate : true
api :
insecure : true
dashboard : true
在之前的配置中,Traefik 正在侦听端口 80。接下来,让我们为部署的 httpbin 应用定义路由和服务。
http :
routers :
guest-router :
entryPoints :
- web
rule : Host(`localhost`)
service : httpbin-service
services :
httpbin-service :
loadBalancer :
servers :
- url : http://192.168.1.4:8000/
此配置向运行在 192.168.1.4 实例上的 httpbin 发送请求。这个配置需要复制到services文件夹作为config.yml.在这之后,你可以查找 http://localhost。浏览器应该加载应用。展开的配置可以在 Traefik 仪表板上看到(参见图 5-4 )。
img/497627_1_En_5_Fig4_HTML.jpg
图 5-4
httpbin 入口点的仪表板
Traefik 日志
Traefik 报告有关遇到的问题的信息。默认情况下,Traefik 将这些报告给标准输出。这些报告的问题对应于 Traefik 应用中的事件。信息以不同的严重级别报告。您可以通过添加log配置来配置 Traefik 日志。该配置可以设置特定文件的日志记录。它还可以指定消息的最低严重级别。
entryPoints :
web :
address : ":80"
providers :
removed for Brevity
log:
level: INFO
filePath: traefik.json.log
format: json
该代码执行以下操作。
将日志定向到当前工作目录中的tarefik.json.log文件
将默认日志级别更改为 INFO ,,它写入致命、错误、警告和信息级别的消息
以 JSON 格式记录消息
默认情况下,Traefik 以通用日志格式写入所有消息。或者,您可以将其更改为 JSON 格式,如图所示。Traefik 可以报告调试、信息、警告、错误和致命级别的日志消息。配置较低的级别可以报告高于配置级别的所有严重级别。
定义的代码是用于启动 Traefik 的静态配置的一部分。Traefik 不会自动加载这些更改。进行更改后,重新启动服务器。您可以如下所示跟踪日志文件。
ch05 $ tail -f traefik.json.log
{"level":"info","msg":"Traefik version 2.2.0 built on 2020-03-25T17:17:27Z","time":"2020-06-13T20:27:08+05:30"}
{"level":"info","msg":"\nStats collection is disabled.\nHelp us improve Traefik by turning this feature on :)\nMore details on: https://docs.traefik.io/contributing/data-collection/\n","time":"2020-06-13T20:27:08+05:30"}
{"level":"error","msg":"unsupported access log format: \"foobar\", defaulting to common format instead.","time":"2020-06-13T20:27:08+05:30"}
{"level":"error","msg":"Failed to create new HTTP code ranges: strconv.Atoi: parsing \"foobar\": invalid syntax","time":"2020-06-13T20:27:08+05:30"}
{"level":"info","msg":"Starting provider aggregator.ProviderAggregator {}","time":"2020-06-13T20:27:08+05:30"}
{"level":"info","msg":"Starting provider file.Provider {\"directory\":\"/Users/rahulsharma/Projects/traefik-book/ch05/code\",\"watch\":true,\"filename\":\"config\",\"debugLogGeneratedTemplate\":true}","time":"2020-06-13T20:27:08+05:30"}
{"level":"info","msg":"Starting provider traefik.Provider {}","time":"2020-06-13T20:27:08+05:30"}
访问日志
Traefik 可以报告有关客户端请求的信息。处理完请求后,信息被写入access log。但是access log不是默认创建的。access log配置设置对特定文件的日志记录。但是默认访问日志是以通用日志格式编写的。可以将其配置为以 JSON 格式报告。
Removed for Brevity
log:
level: INFO
filePath: traefik.json.log
format: json
accessLog:
filePath: access.json.log
format: json
该代码执行以下操作。
将访问日志定向到当前工作目录中的access.json.log文件
以 JSON 格式记录消息
添加上述配置后,重新启动 Traefik 服务器。当我们访问 http://localhost/时,会生成以下访问日志。
logs $ tail -f access.json.log
{"ClientAddr":"[::1]:63226","ClientHost":"::1","ClientPort":"63226","ClientUsername":"-","DownstreamContentSize":12026,"DownstreamStatus":200,"Duration":28245000,"OriginContentSize":12026,"OriginDuration":28187000,"OriginStatus":200,"Overhead":58000,"RequestAddr":"localhost","RequestContentSize":0,"RequestCount":1,"RequestHost":"localhost","RequestMethod":"GET","RequestPath":"/","RequestPort":"-","RequestProtocol":"HTTP/1.1","RequestScheme":"http","RetryAttempts":0,"RouterName":"httpbin-router@file","ServiceAddr":"192.168.1.4:8000","ServiceName":"httpbin-service@file","ServiceURL":{"Scheme":"http","Opaque":"","User":null,"Host":"192.168.1.4:8000","Path":"/","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":""}
TRUNCATED }
访问日志包含各种信息。使用以下报告的属性有助于确定停机和缓慢响应时间。
Duration:处理一个请求所花费的总时间
OriginDuration:从建立连接到从上游服务器接收到响应体的最后一个字节所花费的时间
Overhead:从上游服务器收到的响应和发送回客户端的响应之间的时间差
OriginStatus:上游服务器发送的响应码
"Duration":28245000,
"OriginContentSize":12026,
"OriginDuration":28187000,
"OriginStatus":200,
"Overhead":58000,
因为访问日志是在请求处理之后写入的,所以会增加开销。但是可以通过为日志消息配置缓冲区来优化日志开销。缓冲区支持日志消息的异步写入,而不是请求后写入。缓冲区指定 Traefik 在将日志行写入所选输出之前保留在内存中的日志行数。要启用缓冲区,请配置buffersize属性。
Note
访问日志是仅用于 HTTP 服务的全局配置。这不是入口点或特定于路由的配置。启用后,Traefik 会为所有入口点/用户请求生成日志。
日志过滤器
Traefik 访问日志描述了服务器处理的每个请求。信息很详细。如果服务器正在处理许多用户请求,访问日志会增长得非常快。大量信息很快变得无人管理。或者,您可以根据预配置的标准记录选择性请求。这确保了我们只查看相关的用户请求。它从访问日志中排除无关紧要的日志条目。使用 filters 属性启用选择性日志记录。过滤器属性提供了以下三个选项。
statusCodes:仅记录指定的响应代码列表。
retryAttempts:有重试尝试时的日志
minDuration:当请求花费的时间超过指定时间时记录日志
Removed for Brevity
accessLog:
filePath: logs/access.json.log
format: json
bufferingSize: 50
filters:
statusCodes:
- 200
- 300-302
retryAttempts: true
minDuration: 5s
当下列任一条件为真时,此代码写入访问日志。
响应代码是 200/300/301/302
使用断路来撤回请求
请求时间超过 5 秒
访问http://localhost/应该生成一条日志消息,因为状态代码是 200。现在接入http://localhost/status/418。不应该有任何日志语句。
logs $ tail -f access.json.log
{"ClientAddr":"[::1]:64020","ClientHost":"::1","ClientPort":"64020","ClientUsername":"-","DownstreamContentSize":12026,"DownstreamStatus":200,"Duration":27516000,"OriginContentSize":12026,"OriginDuration":27467000,"OriginStatus":200,"Overhead":49000,"RequestAddr":"localhost","RequestContentSize":0,"RequestCount":1,"RequestHost":"localhost","RequestMethod":"GET","RequestPath":"/","RequestPort":"-","RequestProtocol":"HTTP/1.1","RequestScheme":"http","RetryAttempts":0,"RouterName":"httpbin-router@file","ServiceAddr":"192.168.1.4:8000","ServiceName":"httpbin-service@file"...... TRUNCATED }
日志字段
之前,我们讨论了如何登录响应标准。但是 Traefik 也可以配置为在日志语句中报告选择性信息。您可能需要隐藏用户身份、删除敏感信息或优化日志。Traefik 日志信息由以下两种类型组成。
请求头:用户在请求中传递的头
字段:Traefik 添加的附加信息
这两种信息类型都具有可由以下选项控制的属性。
keep在日志中按原样报告信息。
drop从日志中删除信息。
redact替换和屏蔽日志中的信息。
accessLog:
filePath: logs/access.json.log
format: json
bufferingSize: 50
fields:
defaultMode: keep
names:
ClientUsername: drop
headers:
defaultMode: keep
names:
User-Agent: redact
Authorization: drop
Content-Type: keep
在这段代码中,我们配置了以下内容。
defaultmode的keep值允许报告字段和标题。
defaultmode的keep值启用报告标题。
ClientUsername的drop值将其从日志中删除。
Content-Type和Authorization的drop值从日志中删除这些头。
User-Agent的redact值将该值报告为redacted。
添加上述配置后,重新启动 Traefik 服务器。当您访问 http://localhost/时,会生成以下访问日志。
logs $ tail -f access.json.log
{"ClientAddr":"[::1]:49537","ClientHost":"::1","ClientPort":"49537",
,"origin_X-Processed-Time":"0","request_Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9","request_Accept-Encoding":"gzip, deflate, br","request_Accept-Language":"en-US,en;q=0.9","request_Cache-Control":"max-age=0","request_Connection":"keep-alive","request_Sec-Fetch-Dest":"document","request_Sec-Fetch-Mode":"navigate","request_Sec-Fetch-Site":"none","request_Sec-Fetch-User":"?1","request_Upgrade-Insecure-Requests":"1","request_User-Agent":"REDACTED","request_X-Forwarded-Host":"localhost","request_X-Forwarded-Port":"80","request_X-Forwarded-Proto":"http","request_X-Forwarded-Server":"XE-GGN-IT-02498.local","request_X-Real-Ip":"::1","time":"2020-06-14T16:35:18+05:30"}
Note
Traefik 报告了大约 25 个附加字段。Traefik 文档中提供了字段列表。
原木旋转
生产部署的应用更喜欢日志轮换策略。这有助于在清除历史日志时优化磁盘使用。但是默认情况下 Traefik 日志不会循环。因此,我们需要使用系统程序来执行日志管理。日志管理涉及归档和清除活动。根据操作系统的不同,有不同的程序来完成这项工作。在 FreeBSD 系统上,可以使用newsyslog,而在 Linux 上,可以使用logrotate。它们都依赖于发送 USR1 信号来旋转日志。在下面的讨论中,我们和newsyslog一起工作。概述的步骤对于任何其他程序都是一样的。
FreeBSD 中包含的newsyslog实用程序会循环使用,并在必要时归档日志文件。程序需要输入一个配置文件。该文件标识需要处理哪些日志文件。它提供了一组不同的属性,可以描述文件权限、复制行为、存档数量等。通过使用类似crontab的调度程序,该程序被配置为定期运行。让我们在名为 syslog.conf 的文件中创建以下配置。
/Users/rahulsharma/Projects/traefik-book/ch05/logs/access.json.log rahulsharma:staff 640 5 500 * Z
在这个配置中,我们为acces.json.log配置了日志轮转。
将文件所有者和组设置为rahulsharma:staff。这适用于压缩文件和新的日志文件。
将文件权限设置为 640。
文件只有五次旋转。
当大小增长到 500,000 以上时,会发生旋转。
Z 标志配置压缩文件。
您可以使用下面的命令用描述的配置运行newsyslog。
code $ sudo newsyslog -vf syslog.conf
/Users/rahulsharma/Projects/traefik-book/ch05/logs/access.json.log <5Z>: size (Kb): 532 [500] --> trimming log....
Signal all daemon process(es)...
Notified daemon pid 91 = /var/run/syslog.pid
Pause 10 seconds to allow daemon(s) to close log file(s)
Compress all rotated log file(s)...
Note
前面的过程不适用于 Windows,因为缺少 USR 信号,所以没有 log rotate 程序。
黑名单
Traefik 通过使用中间件来提供对反向列表的支持。我们在第二章中讨论了中间件。它们被配置为路由器的一部分。中间件在规则匹配之后但在将请求转发给服务之前执行。Traefik 通过配置ipWhiteList中间件支持 IP 反向列表。可以使用以下选项进行配置。
sourceRange:以 CIDR 格式描述允许的 IP 集合
ipstrategy:描述如何从X-forward-for头识别客户端 IP
http :
routers :
httpbin-router :
entryPoints :
- web
rule : HostRegexp(`{name:.*}`)
middlewares :
- allowed-sources
service : httpbin-service
middlewares:
allowed-sources:
ipWhiteList:
sourceRange:
- "127.0.0.1/32"
services :
Removed for Brevity
在前面的代码中,我们执行了以下操作。
我们将router rule修改为允许所有主机名使用正则表达式。这是使用HostRegexp函数而不是Host操作符来完成的。
我们添加了带有已配置的ipWhiteList中间件名称的中间件部分。
我们用ipWhiteList.的配置配置了中间件部分
我们使用sourceRange选项添加了允许的 IP 列表。
现在让我们运行配置。访问 http://localhost/页面以访问 httpbin 服务。
$ curl -v http://localhost/
- Trying ::1...
- TCP_NODELAY set
- Connected to localhost (::1) port 80 (#0)
GET / HTTP/1.1
Host: localhost
User-Agent: curl/7.64.1
Accept: /< HTTP/1.1 403 Forbidden
< Date: Sat, 20 Jun 2020 17:41:11 GMT
< Content-Length: 9
< Content-Type: text/plain; charset=utf-8
< - Connection #0 to host localhost left intact
Forbidden* Closing connection 0
我们得到一个被禁止的回应。这是因为我们的本地主机域被解析为 IP6 环回地址(::1)。环回地址不在白名单中。或者,您可以使用 IP4 环回地址(127.0.0.1)来访问。这将按预期加载页面。访问日志中会报告禁止的访问。确保从静态配置中删除基于状态代码的日志过滤器。
{"ClientAddr":"[::1]:64616","ClientHost":"::1","ClientPort":"64616","ClientUsername":"-","DownstreamContentSize":9,"DownstreamStatus":403,"Duration":128000,"OriginContentSize":9,"OriginDuration":79000,"OriginStatus":403,"Overhead":49000,"RequestAddr":"localhost","RequestContentSize":0,"RequestCount":63,"RequestHost":"localhost","RequestMethod":"GET","RequestPath":"/","RequestPort":"-","RequestProtocol":"HTTP/1.1","RequestScheme":"http","RetryAttempts":0,"RouterName":"httpbin-router@file","StartLocal":"2020-06-20T23:11:01.21434+05:30","StartUTC":"2020-06-20T17:41:01.21434Z","entryPointName":"web","level":"info","msg":"","time":"2020-06-20T23:11:01+05:30"}
请求跟踪
你学到了可观察性是一种多样的实践。请求跟踪或分布式跟踪是分析应用行为的重要支柱。它通常应用于分布式系统,以预测跨不同系统的请求处理是如何发生的。它可以指出导致性能问题或请求处理失败的应用。
简而言之,分布式跟踪映射了系统处理请求的流程。处理流是在一个被称为请求跨度的构建块上创建的。请求跨度表示服务处理所花费的时间。所有处理请求的服务都生成各自的跨度。然后,这些跨度被组合成整个请求的单个分布式跟踪。
作为 API 网关,Traefik 接收不同应用的传入请求。它是所有外部请求的单一入口点。Traefik 必须支持请求跨度的生成。生成的请求跨度作为请求头传播到应用。反过来,应用必须进一步传播这些头。Traefik 生成以下 B3 跟踪头。
x-b3-traceid
x-b3 西班牙语
x-b3 亲西班牙语
x-B3-采样
这些跨度被发送到跟踪后端服务。该服务负责存储和处理这些信息。Traefik 支持几个 OpenTracing 后端,如 Zipkin、Datadog 和 Jagger。在本节中,我们将使用 Zipkin。其他后端也需要类似的配置。
安装 Zipkin
Zipkin 是一个用 Java 构建的开源跟踪收集引擎。它不仅支持跟踪收集,还提供了可视化跟踪的仪表板。还有其他允许您分析请求流的特性。由于 Zipkin 是开源的,所以它提供了可以为目标平台编译的代码。或者,我们直接运行一个发布的二进制文件。使用以下命令可以下载 Zipkin 的最新版本。
code $curl -sSL https://zipkin.io/quickstart.sh | bash -s
Thank you for trying Zipkin!
This installer is provided as a quick-start helper, so you can try Zipkin out
without a lengthy installation process.
Fetching version number of latest io.zipkin:zipkin-server release...
Latest release of io.zipkin:zipkin-server seems to be 2.21.4
Downloading io.zipkin:zipkin-server:2.21.4:exec to zipkin.jar...
下载完zipkin.jar后,使用下面的命令运行它。
code $ java -jar zipkin.jar
2020-06-20 21:57:31.012 INFO 47685 --- [ main] z.s.ZipkinServer : Starting ZipkinServer on XE-GGN-IT-02498.local with PID 47685 (/Users/rahulsharma/Projects/trafik/code/zipkin.jar started by rahulsharma in /Users/rahulsharma/Projects/trafik/code)
2020-06-20 21:57:31.016 INFO 47685 --- [ main] z.s.ZipkinServer : The following profiles are active: shared
2020-06-20 21:57:32.040 INFO 47685 --- [ main] c.l.a.c.u.SystemInfo : hostname: xe-ggn-it-02498.local (from 'hostname' command)
2020-06-20 21:57:32.537 INFO 47685 --- [oss-http-*:9411] c.l.a.s.Server : Serving HTTP at /0:0:0:0:0:0:0:0:9411 - http://127.0.0.1:9411/
2020-06-20 21:57:32.538 INFO 47685 --- [ main] c.l.a.s.ArmeriaAutoConfiguration : Armeria server started at ports: {/0:0:0:0:0:0:0:0:9411=ServerPort(/0:0:0:0:0:0:0:0:9411, [http])}
服务器已启动并运行在 9411 端口上。您可以在http://localhost:9411/访问其仪表盘。
img/497627_1_En_5_Fig5_HTML.jpg
图 5-5
Zipkin 仪表板
集成 Zipkin
Traefik 与 Zipkin 的集成非常简单。我们只需要提供 Zipkin API 的位置。这些参数是 Traefik 静态配置的一部分。Traefik 还提供以下属性来自定义跟踪行为。
sameSpan:为 RPC 调用配置一个 span
id128Bit:生成 128 位跟踪 id
samplerate:被追踪请求的百分比
Removed for Brevity
tracing:
zipkin:
httpEndpoint: http://localhost:9411/api/v2/spans
id128Bit : true
sameSpan: true
在这个配置中,我们提供了 Zipkin API 的位置。我们还为 RPC 客户端和服务器配置了具有相同跨度的 128 位跟踪。现在重新启动服务器。
ch05 $ traefik --configfile traefik.yml
INFO[0000] Configuration loaded from file: /Users/rahulsharma/Projects/traefik-book/ch05/traefik.yml
您可以在 Traefik 仪表板中验证配置(参见图 5-6 )。它应该报告在应用中配置了哪个跟踪后端。
img/497627_1_En_5_Fig6_HTML.jpg
图 5-6
跟踪仪表板状态
Note
跟踪是在全局级别启用的。一旦启用,它将为所有请求生成跟踪,包括仪表板 API。
现在,让我们提几个要求。httpbin 应用(见图 5-7 )提供了几种请求类型。尝试加载 IP、状态代码和重定向请求。Traefik 生成请求跟踪,并将其发送给部署的 Zipkin。
img/497627_1_En_5_Fig7_HTML.jpg
图 5-7
httpbin 应用
你可以跟踪访问日志。Traefik 记录所有传递的请求头,包括生成的 B3 头。
{
Removed for Brevity
"request_User-Agent":"REDACTED",
"request_X-B3-Parentspanid":"12f1ca6cf7671169",
"request_X-B3-Sampled":"1",
"request_X-B3-Spanid":"1704e2a62f95fa8b",
"request_X-B3-Traceid":"12f1ca6cf7671169",
}
Traefik 集成由以下步骤组成。
基于配置的采样率为请求生成 TraceId 和 Span
将跟踪头转发给服务应用
根据响应代码更新跨度
将生成的跟踪范围发送到跟踪后端
现在,您可以加载 Zipkin 仪表板了。仪表板提供了一个 UI 来可视化请求跟踪。您可以搜索过去 15 分钟内的请求。结果页面应该如下所示。tracer/Zipkin 仪表板(见图 5-8 )用蓝色标记所有带有 2XX 或 3XX 回路的轨迹。但是返回代码 4XX /5XX 显示为红色。
img/497627_1_En_5_Fig8_HTML.jpg
图 5-8
请求跟踪
Traefik 指标
Traefik 可以生成应用级别的指标。这些指标必须在后台服务中捕获,以便进行监控和警报通知。Traefik 支持使用最广泛的度量解决方案,如 StatsD、Datadog、Prometheus 等。在当前部分,我们与 Prometheus 合作,作为一家有指标支持的商店。Prometheus 是一个使用 Golang 构建的开源解决方案。Prometheus 可以从 Traefik 中暴露的端点获取指标。它还提供了一个可视化指标的仪表板。普罗米修斯的细节超出了本书的范围。
让我们首先通过添加相关配置来启用 Traefik 指标。需要将配置添加到静态配置文件中。Traefik 提供了以下选项。
存储桶:定义响应延迟的存储桶
addEntryPointLabels:向请求度量添加入口点名称
addServiceLabels:将服务名添加到请求度量中
entryPoint:命名配置为发布度量的入口点
manualrouting:为 prometheus@internal 服务启用自定义路由器
entryPoints :
web :
address : ":80"
Removed for Brevity
metrics:
prometheus:
addEntryPointsLabels: true
addServicesLabels : true
此配置在默认端点上启用指标。这些指标在http://locahost:8080/metrics生成。重新启动服务器并验证 Traefik 仪表板上的配置。
img/497627_1_En_5_Fig9_HTML.jpg
图 5-9
启用指标
配置普罗米修斯
现在我们需要在 Prometheus 中捕获生成的指标。让我们从使用发布页面( https://prometheus.io/download/ )下载最新版本开始。您可以解压缩该版本。但是在启动 Prometheus 服务器之前,我们需要配置端点,这个端点需要报废。这可以通过更新捆绑的prometheus.yml来完成
my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
scrape_timeout is set to the global default (10s).
REMOVED for BREVITY
static_configs:
- targets: ['localhost:8080']
在此配置中,Traefik 端点(localhost:8080)被添加到目标列表中。Prometheus 使用http:// localhot:8080/metrics查找指标。现在使用下面的命令启动 Prometheus。
prometheus-2.19.1.darwin-amd64 $ ./prometheus
level=info ts=2020-06-21T06:14:37.958Z caller=main.go:302 msg="No time or size retention was set so using the default time retention" duration=15d
level=info ts=2020-06-21T06:14:37.959Z caller=main.go:337 msg="Starting Prometheus" version="(version=2.19.1, branch=HEAD, revision=eba3fdcbf0d378b66600281903e3aab515732b39)"
level=info ts=2020-06-21T06:14:37.959Z caller=main.go:338 build_context="(go=go1.14.4, user=root@62700b3d0ef9, date=20200618-16:45:01)"
level=info ts=2020-06-21T06:14:37.959Z caller=main.go:339 host_details=(darwin)
level=info ts=2020-06-21T06:14:37.959Z caller=main.go:340 fd_limits="(soft=2560, hard=unlimited)"
level=info ts=2020-06-21T06:14:37.959Z caller=main.go:341 vm_limits="(soft=unlimited, hard=unlimited)"
level=info ts=2020-06-21T06:14:37.960Z caller=main.go:678 msg="Starting TSDB ..."
level=info ts=2020-06-21T06:14:37.960Z caller=web.go:524 component=web msg="Start listening for connections" address=0.0.0.0:9090
我们可以使用http://locahost:9090/加载普罗米修斯仪表板。指标下拉菜单有不同的选项,前缀为traefik_。我们加载traefik_entrypoint_requests_total度量。它描述了 Traefik 处理的请求总数。此外,您还可以使用以下 bash 脚本向 Traefik 发送几个请求。
$ for ((i=1;i<=10000;i++)); do curl -v --header "Connection: keep-alive" "localhost"; done
该脚本向 Traefik 服务器发送大约 10,000 个请求。最后,您可以查看普罗米修斯仪表盘(见图 5-10 ),它捕捉到了流量的增长。
img/497627_1_En_5_Fig10_HTML.jpg
图 5-10
请求流量度量
摘要
本章讨论了可观测性。我们讨论了它的三个支柱:错误日志、请求跟踪和应用度量。首先,我们配置了错误日志。这些日志捕获关于 Traefik 中发生的任何错误的信息。下一步,我们配置了访问日志。访问日志捕获由 Traefik 处理的传入请求。随着传入请求的增加,访问日志会迅速膨胀。
我们讨论了通过使用过滤器、旋转和标题屏蔽来管理它的方法。我们还配置了IPwhitelist中间件,并捕获了它生成的禁止日志。之后,我们使用 Zipkin 启用了请求跟踪。Traefik 生成用于跟踪的 B3 标头。这些标题可以在访问日志中看到。
您查看了流程流,并在 Zipkin 仪表板中生成了跟踪。最后,我们启用了 Traefik 指标,并在 Prometheus 中捕获了它们。Traefik 支持许多用于跟踪和度量的后端存储。以齐普金和普罗米修斯为例来说明其集成。这些工具在像微服务这样的分布式架构中很有帮助。
在下一章中,您将使用微服务的 Traefik 支持。
六、用于微服务的 Traefik
在第一章中,我们讨论了微服务架构。企业越来越多地从整体架构转向利用微服务架构。但是微服务系统是一个分布式系统。为了有效地使用它,我们需要采用额外的基础设施组件。这些附加组件规定了每个微服务必须遵循的一套新准则。
微服务架构提倡粒度服务来满足不断发展的业务需求。根据不断变化的业务需求,开发团队可以创建或组合服务。此外,在生产环境中,每个服务都是独立部署和扩展的。基于云的自动伸缩通常基于服务负载复制实例。因此,架构处于不断的演进中,并且没有微服务的最终状态列表。
动态生态系统需要最新运行的微服务的目录。这也被称为服务注册中心。简而言之,注册中心是一个包含服务实例和相应位置细节的服务数据库。为了有效地工作,服务必须在启动时注册,在关闭时删除。有许多方法可以实现这一点,但是服务自注册的过程是推荐的机制。
服务在注册中心注册后,客户机需要查找相同的服务。这个客户端过程被称为服务发现(见图 6-1 )。客户端首先查询服务注册中心来查找服务的可用实例。获得活动服务实例列表后,客户端可以向所需的服务发送请求。
img/497627_1_En_6_Fig1_HTML.jpg
图 6-1
服务注册和服务发现
服务注册中心通常是信息的键值存储。很多时候,您必须注册关于服务的附加信息。这些信息可能与多租户系统中的客户端类型有关,也可能与所提供的视图有关,比如 web 或 mobile,或者其他任何信息。每个服务负责将这些数据添加到存储中。在本章中,我们使用服务自注册机制来集成 Traefik 和微服务。
微服务架构推荐服务协作。这意味着一个服务可以调用其他服务来获取用户请求所需的数据。但是这个机制有它自己的一系列问题。当一个服务同步调用另一个服务时,总有可能另一个服务不可用,或者表现出很高的延迟,以至于基本上不可用。
在等待其他服务响应时,调用方可能会消耗线程等宝贵的资源。这可能会导致资源耗尽,从而使调用服务无法处理其他请求。
一个服务的失败可能会影响整个应用中的其他服务。这个问题可以通过在应用设计中采用断路器来解决(见图 6-2 )。当连续失败的次数超过阈值时,断路器跳闸,并且在超时期间,所有调用远程服务的尝试都会立即失败。
img/497627_1_En_6_Fig2_HTML.jpg
图 6-2
断路器模式
超时后,断路器允许有限数量的测试请求通过。如果这些请求成功,断路器恢复正常运行。否则,如果出现故障,超时周期将重新开始。在本章中,我们在调用 Traefik 的不同微服务时集成断路器。
API 网关是任何基于微服务的架构的重要组成部分。诸如身份验证、负载平衡、依赖关系解析、数据转换和动态请求调度等跨领域的问题可以方便而通用地处理。然后,微服务可以专注于特定的任务,而无需重复代码。这使得每个微服务的开发更加容易和快速。
实现一个 API 网关,它是所有客户端的单一入口点。API 网关以两种方式之一处理请求。一些请求被代理/路由到适当的服务。它通过分散到多个服务来处理其他请求。在前面的章节中,我们为单一服务的需求配置了 Traefik。在本章中,我们将 Traefik 配置为微服务网关。
pet-临床应用
在本章中,我们需要一个基于微服务的应用。应用必须至少有两个或更多与 Traefik 集成的微服务。我们使用 PetClinic 应用。PetClinic 是一个基于 Java 的应用,它与 Spring 框架打包在一起,用于学习目的。Spring 社区维护这个应用。它详细解释了基于 Spring 框架的技术。因此,该应用是企业技术的良好测试平台。
PetClinic 应用是为兽医诊所的需要而设计的。该应用使其用户能够查看和管理兽医、客户和他们的宠物。该应用支持以下用例。
查看兽医及其专业的列表
查看宠物主人的信息
更新宠物主人的信息
在系统中添加一个新的宠物主人
查看宠物的信息
更新宠物的信息
向系统中添加新宠物
查看宠物的访问历史信息
向宠物的访问历史记录中添加有关访问的信息
该解决方案需要使用微服务架构来构建。让我们从 https://github.com/rahul0208/spring-petclinic-microservices 下载 PetClinic 应用。
Spring PetClinic 微服务是围绕小型独立服务(几百行代码)构建的,运行在自己的 JVM 中,通过 REST API 在 HTTP 上通信。这些微服务都是用 Java 写的。三个客户、vet 和访问业务微服务中的每一个都是 Spring Boot 意义上的应用。为了在分布式环境中工作,这些微服务依赖于 Spring Cloud 提供的一组工具:集中配置管理、自动发现其他微服务和负载平衡(见图 6-3 )。应用 UI 用 Angular 开发,部署在 Nginx。Traefik 将执行请求路由。在本章中,我们将构建和部署一个应用。书中涵盖了一些重要的方面,但是完整的应用技术细节超出了本书的范围。
img/497627_1_En_6_Fig3_HTML.jpg
图 6-3
宠物诊所服务
应用配置
PetClinic 应用配置位于 https://github.com/rahul0208/spring-petclinic-microservices-config 。使用 spring-cloud-config 服务器提供配置。配置服务器在以下 REST URLs 上提供配置。
/{应用}/{个人资料}[/{标签}]
/{应用}-{个人资料}。阳明海运股份有限公司
/{标签}/{应用}-{个人资料}。阳明海运股份有限公司
/{应用}-{个人资料}。性能
/{标签}/{应用}-{个人资料}。性能
配置服务器还消除了在配置改变的情况下重新打包应用的需求。由于所有最新的配置在列出的 REST 端点上都是可用的,所以我们只需要重启服务。还可以通过使用@RefreshScope注释或EnvironmentChangeEvent事件监听器来配置热重载服务。
Spring 遵循从application.properties.加载应用配置的惯例,但是如前所述,当使用 spring-cloud-config 服务器时,application.properties不再是应用的一部分。相反,spring-cloud-context 被配置为从外部源加载配置属性。它也可以配置为解密外部配置文件中的属性。Spring Cloud 应用启动一个 bootstrap 上下文,从 bootstrap.yml 文件中加载它的配置。bootstrap.yml 文件非常简洁。它包含微服务的名称和配置服务器的 URL。以下是来自 vets-service 微服务的一个例子。
spring:
cloud:
config:
uri: http://localhost:8888
application:
name: vets-service
我们在配置中指定了 spring-config-server 在 localhost:8888 的位置。vets-service 微服务要求服务器位于上述位置。它查询服务器以确定配置值,然后完成服务器启动。值得注意的是,可以使用 SPRING_CLOUD_CONFIG 这样的环境变量来注入配置服务器的位置。但是不能使用服务注册表来发现它。
咨询服务登记处
之前,我们讨论了微服务架构的演进本质。当服务部署在云中时,您很难预测相同微服务的实例数量(取决于负载)或它们部署在哪里(以及它们可以在哪个 IP 和端口上访问)。因此,需要一个服务注册中心。在 PetClinic 应用中,我们使用了咨询服务注册中心。启动时,每个微服务都向服务注册表注册自己。注册后,每个服务定期向注册中心提供心跳。这本书并不打算涵盖咨询服务注册的细节。有关更多信息,请参考文档。
现在让我们从 www.consul.io/ 下载领事。下载后,提取压缩文件并启动服务。
$ ./consul agent -dev
==> Starting Consul agent...
Version: 'v1.8.0'
Node ID: '935fccd6-74ca-e62e-c53f-c838de3c3681'
Node name: 'XE-GGN-IT-02498.local'
Datacenter: 'dc1' (Segment: '')
Server: true (Bootstrap: false)
Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, DNS: 8600)
Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false, Auto-Encrypt-TLS: false
==> Log data will now stream in as it occurs:
我们可以在http://localhost:8500/ui/dc1/services加载 Consul UI。(见图 6-4 )
img/497627_1_En_6_Fig4_HTML.jpg
图 6-4
领事服务
部署宠物诊所
现在我们可以以任何顺序运行 PetClinic 微服务。该应用由以下三个微服务组成。
兽医服务
探访服务
客户服务
所有的服务都基于 Spring Boot。它们被打包成可执行的 JAR 文件。执行特定于服务的 jar 启动带有嵌入式 servlet 引擎的服务。因为服务获取是来自 spring-config 服务器的配置,所以让我们确保我们是 bootstrap.yml 中配置服务器的正确位置。
接下来,确保 config-server 指向正确的 git 位置。在本例中, https://github.com/rahul0208/spring-petclinic-microservices-config 是应用配置源。我们建议您克隆此配置,并根据环境进行更新。
前面的 URL 是在 config-server 的 bootstrap.yml 中配置的。
server.port: 8888
spring:
cloud:
config:
server:
git:
uri: https://github.com/spring-petclinic/spring-petclinic-microservices-config
native:
searchLocations: file:///${GIT_REPO}
配置的细节超出了本书的范围。我们建议您用自己的克隆更新 URI。现在,我们需要使用包 maven 包装器来构建服务。
$ ./mvnw clean install
[INFO] Scanning for projects...
[INFO] --------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] spring-petclinic-microservices [pom]
[INFO] spring-petclinic-customers-service [jar]
[INFO] spring-petclinic-vets-service [jar]
[INFO] spring-petclinic-visits-service [jar]
[INFO] spring-petclinic-config-server [jar]
...
..... Truncated for Brevity
该命令在目标文件夹下为每个服务创建一个可执行文件。
$ find . -type f -name "*jar"
./spring-petclinic-config-server/target/spring-petclinic-config-server-2.3.1.jar
./spring-petclinic-ui/target/spring-petclinic-ui-2.3.1.jar
./spring-petclinic-vets-service/target/spring-petclinic-vets-service-2.3.1.jar
./.mvn/wrapper/maven-wrapper.jar
./spring-petclinic-customers-service/target/spring-petclinic-customers-service-2.3.1.jar
./spring-petclinic-visits-service/target/spring-petclinic-visits-service-2.3.1.jar
在当前设置中,我们将所有服务部署在同一个盒子中。因此,在配置中使用本地主机地址。通过在 git 配置中进行适当的更新,您可以在任何主机上自由部署服务。作为第一步,您需要用下面的命令启动配置服务器。
target $ java -jar spring-petclinic-config-server-2.3.1.jar
2020-07-26 21:56:26.401 INFO 7442 --- [ main] o.s.s.p.config.ConfigServerApplication : No active profile set, falling back to default profiles: default
2020-07-26 21:56:27.221 INFO 7442 --- [ main] o.s.cloud.context.scope.GenericScope : BeanFactory id=15cd0375-3bcf-3529-9d02-67397a0dc277
2020-07-26 21:56:27.609 INFO 7442 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8888 (http)
2020-07-26 21:56:27.621 INFO 7442 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-07-26 21:56:27.621 INFO 7442 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.36]
2020-07-26 21:56:27.690 INFO 7442 --- [ main]
下一步是运行每个微服务。但是首先让我们确保我们在application.yml.中有正确的咨询服务注册地址
spring:
cloud:
consul:
host: localhost
port: 8500
现在让我们用下面的命令启动 vets 服务。
target $ java -jar spring-petclinic-vets-service-2.3.1.jar
2020-07-21 15:34:11.693 INFO [vets-service,,,] 26509 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://localhost:8888
2020-07-21 15:34:15.525 INFO [vets-service,,,] 26509 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Located environment: name=vets-service, profiles=[default], label=null, version=062fb94b71dc6b99e6518fe7088a0bff3a9431d1, state=null
2020-07-21 15:34:15.527 INFO [vets-service,,,] 26509 --- [ main] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-configClient'}, BootstrapPropertySource {name='bootstrapProperties-https://github.com/spring-petclinic/spring-petclinic-microservices-config/vets-service.yml (document #1)'}, BootstrapPropertySource {name='bootstrapProperties-https://github.com/spring-petclinic/spring-petclinic-microservices-config/vets-service.yml (document #0)'}, BootstrapPropertySource {name='bootstrapProperties-https://github.com/spring-petclinic/spring-petclinic-microservices-config/application.yml (document #0)'}]
- Start completed.
同样,我们需要启动客户服务和访问服务。每个服务都在 Consul 服务注册表中注册自己。您可以在咨询仪表板中验证服务详情(参见图 6-5 )。此外,这些服务中的每一个都可能报告基于 Zipkin 的请求跟踪失败。请求跟踪提供了各种好处。在第五章中,我们介绍了这些工具与 Traefik 的集成。我们在本章中不讨论这些集成。
img/497627_1_En_6_Fig5_HTML.jpg
图 6-5
仪表板 UI
宠物诊所用户界面
应用 UI 是在 AngularJS 上使用 1.7 版本构建的。这些 HTML 页面被部署为 Spring web 应用的静态资源。或者,我们可以将它们打包并部署在像 Apache Tomcat 或 HTTPD 这样的服务器上。UI 也被打包成一个可执行的 JAR。现在让我们用下面的命令启动 UI。
target $ java -jar spring-petclinic-ui-2.3.1.jar
2020-07-27 22:15:21.996 INFO [petclinic-ui,,,] 17732 --- [ restartedMain] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://localhost:8888
2020-07-27 22:15:22.870 INFO [petclinic-ui,,,] 17732 --- [ restartedMain] c.c.c.ConfigServicePropertySourceLocator : Located environment: name=petclinic-ui, profiles=[default], label=null, version=8adeb754f96df6e7308344e7bb2ceddcca09b93f, state=null
2020-07-27 22:15:22.871 INFO [petclinic-ui,,,] 17732 --- [ restartedMain] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-configClient'}, BootstrapPropertySource {name='bootstrapProperties-https://github.com/rahul0208/spring-petclinic-microservices-config/petclinic-ui.yml (document #0)'}, BootstrapPropertySource {name='bootstrapProperties-https://github.com/rahul0208/spring-petclinic-microservices-config/application.yml (document #0)'}]
...... TRUNCATED FOR BREVITY
UI 部署在 9000 端口上。我们可以用http://localhost:9000/#!/welcome来访问它。(见图 6-6
img/497627_1_En_6_Fig6_HTML.jpg
图 6-6
PetClinic UI(小诊所 UI)
UI 也在更新咨询服务注册中心。这样,注册表就是正在运行的服务的综合目录。
配置网关
在前面的设置中,我们部署了应用的所有微服务。现在我们需要配置 Traefik 来呈现 UI,并将调用路由到每个服务。我们可以像前面几章一样,把这个配置写在一个文件中。它配置 Traefik,但是这种方法不能处理微服务架构的动态特性。随着新的服务加入到生态系统中,我们需要不断更新配置。此外,很难保持更新服务的所有实例的 IP 地址。
或者,Traefik 可以作为配置提供者与 Consul 键值存储一起使用。所有 Traefik 配置都作为键和值分层添加到已配置的根节点下。默认根节点名为“traefik”。表 6-1 突出显示了一些 Traefik 键。
表 6-1
交通钥匙
|
钥匙
|
价值
|
| --- | --- |
| traefik/http/routers//entry points/0 | 指定各自的入口点名称 |
| traefik/http/routers//middleware/0 | 指定中间件名称 |
| traefik/http/routers/ /rule | 指定了匹配规则 |
| traefik/http/routers//service | 指定各自的服务名称 |
| traefik/http/service//load balancers/0/URL | 指定了实例 url 位置 |
| RAE fik/http/middleware//strip prefix/prefix/0 | 指定了中间件配置 |
Traefik 文档提供了用于配置它的密钥的更新列表。作为第一步,让我们将配置添加到 Consul。您可以使用 Consul GUI 创建它。或者,您可以从 JSON 文件中导入键。每个键的值都以 Base64 格式编码。
$ consul kv import "$(cat config.json)"
Imported: traefik/http/middlewares/petclinic-customers-stripprefix/stripPrefix/prefixes/0
Imported: traefik/http/middlewares/petclinic-visits-stripprefix/stripPrefix/prefixes/0
Imported: traefik/http/routers/petclinic-customers-route/entryPoints/0
Imported: traefik/http/routers/petclinic-customers-route/middlewares/0
Imported: traefik/http/routers/petclinic-customers-route/rule
Imported: traefik/http/routers/petclinic-customers-route/service
Imported: traefik/http/routers/petclinic-route/entryPoints/0
Imported: traefik/http/routers/petclinic-route/rule
Imported: traefik/http/routers/petclinic-route/service
Imported: traefik/http/routers/petclinic-vets-route/entryPoints/0
Imported: traefik/http/routers/petclinic-vets-route/rule
Imported: traefik/http/routers/petclinic-vets-route/service
Imported: traefik/http/routers/petclinic-visits-route/entryPoints/0
Imported: traefik/http/routers/petclinic-visits-route/middlewares/0
Imported: traefik/http/routers/petclinic-visits-route/rule
Imported: traefik/http/routers/petclinic-visits-route/service
一旦配置被导入,我们应该在 Consul 存储中看到所有的键值。现在我们需要更新 Traefik 静态配置来使用 Consul provider。
entryPoints :
web :
address : ":80"
providers:
consul:
endpoints:
- "127.0.0.1:8500"
rootKey : "traefik"
api :
insecure : true
dashboard : true
我们在配置中指定了 Consul provider 而不是 FileProvider。我们还指定了位置和根密钥。还有其他选项来配置身份验证和 TLS 信息。让我们启动 Traefik 服务器并查看仪表板。
仪表板显示来自 Consul 键值存储的配置。Traefik 已经创建了四条新路由,每条路由对应一个已部署的服务。如果服务没有运行,那么配置添加的路由是错误的,如图 6-7 所示。如果您单击某个路径,您会看到一条关于相应服务缺失详细信息的错误消息。
img/497627_1_En_6_Fig7_HTML.jpg
图 6-7
来自 consul 的流量配置
服务详情
Traefik 需要每个服务的服务器详细信息。在微服务架构中,服务注册是将所有细节添加到注册表中的过程。在我们看来,自注册是支持所有可能场景的最简单的机制。我们扩展了自助注册,为所需的咨询密钥添加了 Traefik 特定的详细信息。ServiceRegistry类完成了这一职责。
@Configuration
public class ServiceRegistry implements ApplicationListener {
final String serviceKey = "/traefik/http/services/{0}/loadBalancer/servers/";
final String serverKey = "/traefik/http/services/{0}/loadBalancer/servers/{1}/";
final String urlKey = "/traefik/http/services/{0}/loadBalancer/servers/{1}/url";
@PreDestroy
void removerServerMapping() {
if(index > -1) {
consulClient.deleteKVValues(format(serverKey, applicationName, index));
}
}
void addServerMapping(int port) {
Response<List<String>> keys = consulClient.getKVKeysOnly(format(serviceKey, applicationName));
index = keys.getValue()!=null ? keys.getValue().size() : 0;
consulClient.setKVValue(format(urlKey, applicationName,index), format("http://{0}:{1,number,#}/","127.0.0.1",port));
}
// REMOVED for Brevity
}
前面的代码执行以下操作。
已确定启动应用的端口
使用 ConsulClient 将主机和端口信息添加到 Consul KV 存储中
将值添加到 Traefik keys/Traefik/http/services/{ 0 }/load balancer/servers/{ 1 }/URL 中
服务关闭时删除密钥
类是每个服务的一部分。如果您启动所有服务并重新加载仪表板,您会看到所有错误都已修复。(见图 6-8
img/497627_1_En_6_Fig8_HTML.jpg
图 6-8
PetClinic 配置
Note
Traefik 继续关注 KVS 商店的价值。当配置在 Consul 中更新时,它会自动重新加载配置。
现在让我们在 http://localhost/上加载 PetClinic 应用。应用按预期执行。我们可以跨三种不同的微服务加载和保存数据。(参见图 6-9 )
img/497627_1_En_6_Fig9_HTML.jpg
图 6-9
宠物诊所
断路器
我们说过,微服务经常协作来交付一个完整的用户功能。一个服务调用其他服务来获取相关数据。但是在像微服务这样的分布式系统中,远程服务调用在失败之前可能会挂起一段时间。无响应的服务调用会阻塞调用服务的资源。如果有许多这样的调用,系统可能会耗尽关键资源,导致跨多个系统的级联故障。
断路器经常被用来解决这个故障快速行为的问题。断路器跟踪远程呼叫。在不正常的响应中,断路器会立即返回,而不会将呼叫发送到目标服务。这本书没有详细介绍这种模式。
Traefik 提供了可以配置断路器的中间件。因为断路器被配置为中间件链的一部分,所以断路器仅在其执行后改变行为。值得注意的是,尽管断路器在配置中声明了一次,但它配置为每个路由的单独实例。Traefik 可以通过以下指标检测服务错误率。
延迟 : Traefik 可以测量服务分位数延迟时间。如果测量的时间超过一个配置值(如LatencyAtQuantileMS(50.0) > 100 ) .该参数指定分位数,则断路器可被触发。
网络错误:测量网络错误率(如NetworkErrorRatio() > 0.30)。
响应代码 : Traefik 可以测量服务响应状态代码(如ResponseCodeRatio(500, 600, 0, 600) > 0.25 ) .这里的四个参数是 HTTP 状态代码。
错误状态代码来自
错误状态代码至
申请状态代码自
申请状态代码至
这些度量值中的每一个都可以通过使用以下操作符来检查。
大于(>)
大于或等于(> =)
小于(
小于或等于(< =)
等于(==)和
不等于(!=)运算符
您还可以使用 AND (&&)和 OR (||)运算符组合两个或多个指标。当 Traefik 确定触发了断路器时,它不会将呼叫转发到目标服务;相反,它返回 503 响应。(参见图 6-10 )
img/497627_1_En_6_Fig10_HTML.jpg
图 6-10
Referrer Policy: no-referrer-when-downgrade
img/497627_1_En_6_Fig13_HTML.jpg
图 6-13
节气门仪表板
中间件链
Traefik 提供了一个链式中间件,可用于简化跨不同服务应用的配置。链式中间件可以用来按顺序对中间件进行分组。整个组可以应用于一个路由,消除了单独应用每个中间件的需要。通过向链中间件属性提供一个逗号分隔的列表来指定完整的链。在这个场景中,我们可以将链配置为由断路器和速率限制中间件组成。
$ consul kv import "$(cat chain-list.json)"
Imported: traefik/http/middlewares/chain-list/chain/middlewares
Imported: traefik/http/routers/petclinic-customers-route/middlewares/0
Traefik 更新配置并显示在仪表板中,如图 6-14 所示。
img/497627_1_En_6_Fig14_HTML.jpg
图 6-14
链式中间件
金丝雀部署
Traefik 使用加权循环法支持 Canary 部署。在上一节中,我们添加了ServiceRegistry类来添加实例细节。这些实例以循环方式使用。在第三章中,我们讨论了加权循环法,权重被添加到服务器实例中。Traefik 按照提供权重的比率划分收到的请求。当我们开始新的实例时,你会在咨询服务注册中心看到新的服务(见图 6-15 )。可以从 UI 为每个实例添加权重。
img/497627_1_En_6_Fig15_HTML.jpg
图 6-15
多个实例
将请求子集重定向到新服务是 canary 部署的基础之一。该机制可以通过使用 Consul 客户端来实现自动化。但是完整的端到端流程需要额外的组件来提供和部署新发布的版本。
Note
本章将 Traefik 与 Consul KV store 集成在一起。Traefik 还提供了与 Zookeeper 和 etcd 的集成。与他人的配置类似于基于 Consul 的集成,但是有些特性不能像预期的那样工作。
摘要
在本章中,我们部署了一个基于微服务的解决方案,并将 Traefik 配置为它的网关。我们使用了微服务的咨询服务注册。启用 Traefik 从 Consul KV 存储中读取配置。Traefik 可以检测 KV 存储中的更新并执行热重装。这使得配置在像微服务这样的动态生态系统中保持更新。
服务可以在它们启动/关闭时注册/注销它们。这些更新由 Traefik 挑选,它提供了可以在微服务架构中配置的中间件。本章介绍了断路器、重试、速率限制和链式中间件。它还研究了基于加权轮询的部署,这种部署可以拆分 canary 部署的请求。在下一章中,我们将在编排引擎上部署微服务解决方案并配置 Traefik。
七、作为 Kubernetes 入口的 Traefik
在最后一章中,您将尝试 Traefik 与 Kubernetes 容器编排平台的本机集成。Kubernetes 无疑是最受欢迎的容器微服务平台。Traefik 与 Kubernetes 紧密结合,可以充当 Kubernetes 生态系统中的一等公民。您已经尝试了 Traefik 的许多网关功能。本章探讨了这些功能如何轻松地映射到 Kubernetes 入口概念。您还可以看到 Traefik 与 Jaeger 的简单集成,Jaeger 是一个特定于 Kubernetes 的分布式跟踪解决方案。
Note
与前几章一样,重点仍然是 Traefik 如何与 Kubernetes 生态系统集成,而不是它的高级特性。我们假设您对 Kubernetes 原语有基本的了解,因此我们只解释 Traefik 如何映射到它们。
trafik 作为 kubernetes 入口控制器
Kubernetes 主要是一个 orchestrator/scheduler 和一个支持所需状态配置的 API 服务器。通常,客户端向 Kubernetes API 服务器提交声明性的 YAML 资源请求,Kubernetes 相应地提供所请求的资源。对于 Kubernetes API 服务器支持的每一种资源类型,比如部署或服务,默认的 Kubernetes 控制器会处理和提供提交的资源请求。一个例外是入口资源类型。
入口对象在单个资源中定义流量路由规则(例如,负载平衡、SSL 终止、基于路径的路由),以在集群外部公开多个服务。根据 Kubernetes 官方文档:
Ingress 是允许入站连接到达后端定义的端点的规则集合。入口可以被配置为向服务提供外部可达的 URL、负载平衡流量、终止 SSL ,提供基于名称的虚拟主机等。
这与 Traefik 充当 API 网关的方式没有太大区别。入口控制器是负责提供提交的入口请求的组件。Kubernetes 不附带默认入口控制器。第三方供应商提供实现。有一个参考(也是广泛使用的)基于 nginx 的 nginx-ingress-controller(它有三个不同的版本),但是最终用户可以自由部署他们喜欢的任何其他 ingress 控制器。在本章中,我们将 Traefik 作为入口控制器的选择。
Traefik 在早期版本中支持 Ingress API。但是在 Traefik v2 中,他们对配置方法做了一些更改。这是由于入口 API 中的限制,这些限制困扰着规范。
Ingress API 规范花了很长时间进行测试。自 Kubernetes 版本 1.2 以来,它一直处于 v1beta1 状态。该规范仅在 2019 年正式定稿,并在 Kubernetes 版本中获得 GA 地位。基本规范相当简单,但是在各种各样的实现中制定一致的标准是一个挑战。没有好的方法来传递特定于供应商的配置,以针对特定的实现进行微调。实现者开始在 YAML 定义中的许多自定义注释中定义特定于供应商的配置,导致了空间的碎片化。Traefik 的旧版本遵循相同的方法。还有一些歧义,比如结尾的“/”处理得不一致(也有问题)。最终,规范是不精确的,既不是可移植的,也不是功能丰富的,所以拥有一个标准 API 的初衷从未实现。许多第三方供应商没有采用 ingress 规范,而是将 LoadBalancer 服务类型与他们自己的定制配置一起使用——同样是通过注释。一旦规范最终确定,大使 API Gateway 将在 2019 年建立入口支持。
大多数 Kubernetes 社区现在已经脱离了定制注释(对于其他用例也是如此)和入口规范,而转向定制资源定义 (CRDs)。CRD 在集群中定义了一个新的对象种类,并让 Kubernetes API 服务器处理它的生命周期。Kubernetes 是一个对扩展开放的系统,允许外部实现者定义他们的自定义资源 API 定义,并运行自定义控制器来处理这些自定义 API 资源。
第三方的 ingress 实现被重组为他们定义的 CRD 的 Kubernetes 控制器。Contour 是首批引入 CRD 进行配置的入口 API 网关之一。甚至 Ambassador Gateway 现在也推荐使用自己的 CRDs,而不是新的 ingress 支持。Traefik 紧随其后,Traefik v2 引入了 IngressRoute(和其他)CRD,为在 Kubernetes 中配置 Traefik 路由提供了一种更好的方式。
Traefik 现在有两个独立的 Kubernetes 提供商。一个是传统的 Kubernetes ingress 提供程序,其中 Traefik 的行为与任何其他 Kubernetes ingress 控制器完全一样,并使用许多自定义注释。另一个是 Kubernetes CRD 提供商,这是我们本章的重点。Traefik 的所有入口配置都使用 Traefik 的 IngressRoute 和其他 CRD 提交给 Kubernetes。这为在 Kubernetes 上配置 Traefik 提供了更好的体验,是目前推荐的方法。
Note
在这一章中,服务这个词是重载的。服务的常见含义是公开 API 的应用,这是我们通常使用的术语。在 Traefik 配置的上下文中还有一个服务,它指向一个实际的后端服务。在这一章中,还有一个 Kubernetes 服务,这就是 Kubernetes 如何将流量路由到 pods。为了避免混淆,我们明确地说明了所有事件的含义。
在 kubernetes 安装 trafik
在本章中,我们将在一台笔记本电脑上运行一个本地 Kubernetes 集群来进行 Traefik 安装和配置。本地 Kubernetes 的安装不在本书讨论范围之内,但是从官网设置应该不会太复杂。我们在不同的场景下使用 microk8s ( https://microk8s.io/ )(在 macOS 上运行多次)或 minikube ( https://minikube.sigs.k8s.io/docs/ )。
Microk8s 使得一些高级 Kubernetes 应用的设置变得非常简单,而 minikube 是运行 Kubernetes 本地版本的事实上的标准。您可能更喜欢使用您喜欢的任何其他 Kubernetes 风格,例如启用了 Kubernetes 的 Docker Desktop。您也可以使用基于云的托管产品。此外,我们针对后面的几种情况转向托管云产品。
我们预计本地和云 Kubernetes 之间的 Traefik 配置和安装不需要任何更改。使用云产品的原因是为了再次使用公共 TLS 证书(类似于第四章)。对于使用 Let's Encrypt 的 TLS 终止,我们在 DigitalOcean (DOKS)上提供了一个云 Kubernetes 集群。如果你愿意的话,你可以选择使用其他品牌,如 GKE、AKS 或 EKS。我们概述的大多数步骤应该可以在任何 Kubernetes 发行版上按原样工作。
我们已经运行了本地的 Kubernetes 集群。所有对 Kubernetes API 服务器的请求都是通过 kubectl CLI 进行的,它通常与本地 Kubernetes 发行版一起安装。Kubectl 需要一个 kube 上下文配置来指向我们的目标 Kubernetes 集群。我们首先在集群上手动安装 Traefik。然后,我们将在后面的小节中探索一种更简单的安装机制。为了在 Kubernetes 上启动和运行 Traefik,我们需要三部分配置。
Kubernetes RBAC 配置给 Traefik 足够的权限与 API 服务器对话。
我遇到了 CRD
实际的交通部署
Note
这些是标准的 Traefik 部署文件。我们鼓励您在 https://docs.traefik.io/providers/kubernetes-crd 和 https://docs.traefik.io/routing/providers/kubernetes-crd 从 Traefik 文档中获取所有这些的更新版本。
First install the RBAC security configuration
% kubectl apply -f traefik-rbac.yml
clusterrole.rbac.authorization.k8s.io/traefik-ingress-controller created
clusterrolebinding.rbac.authorization.k8s.io/traefik-ingress-controller created
RBAC configuration details, full configuration omitted for brevity
rules:
- apiGroups:
- ""
resources: - services
- endpoints
- secrets
verbs: - get
- list
- watch
- ""
- apiGroups:
- extensions
resources: - ingresses
verbs: - get
- list
- watch
- extensions
- apiGroups:
- extensions
resources: - ingresses/status
verbs: - update
- extensions
- apiGroups:
- traefik.containo.us
resources: - middlewares
- ingressroutes
- traefikservices
- ingressroutetcps
- ingressrouteudps
- tlsoptions
- tlsstores
verbs: - get
- list
- watch
- traefik.containo.us
Listing 7-1Installing Traefik RBAC via kubectl
同样,Kubernetes 提供了一个声明性的 API 服务器,所以清单 7-1 中的 RBAC 配置向 Kubernetes 服务授予 Traefik 读取权限,并在它的自定义资源上使用traefik.containo.us API 组(尽管我们还没有安装 CRDs)。Traefik 可以观察这些类型的对象的变化,并相应地重新配置自己。接下来,我们安装清单 7-2 中的 Traefik CRDs。
% kubectl apply -f traefik-crd.yml
customresourcedefinition.apiextensions.k8s.io/ingressroutes.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/middlewares.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/ingressroutetcps.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/ingressrouteudps.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/tlsoptions.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/tlsstores.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/traefikservices.traefik.containo.us created
IngressRoute CRD
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ingressroutes.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: IngressRoute
plural: ingressroutes
singular: ingressroute
scope: Namespaced
Rest omitted for brevity
Listing 7-2Installing Traefik CRDs via kubectl
我们安装了清单 7-2 中列出的 7 个 CRD。这些都是为不同类型的自定义配置提供的 CRDs Traefik。这个数字将来可能会增加。我们没有挑挑拣拣,而是把它们全都安装了。我们列出了 Ingres route CRD,它在接下来的部分中被大量使用。具体细节不是很有意思。需要安装 CRDs,以便 Traefik 可以监视对 API 服务器的任何这类定制资源请求,并对其进行操作。Traefik 充当 Kubernetes 控制器,对这些自定义资源类型进行操作。根据清单,每种不同类型的 Traefik 特定配置都有不同的 CRD。这是新 CRD 方法带给我们的一大优势。早些时候,很多这样的内容会被定义为标准入口资源上的自定义注释。
接下来,我们安装 Traefik(参见清单 7-3 )。我们将 Traefik 安装为带有一个 pod 的 Kubernetes 部署。Kubernetes 始终保持至少一个 Traefik 实例运行。由于 Traefik 是一个无状态服务,所有配置都来自 Kubernetes,因此即使 pod 重新启动,Traefik 也会保留配置。
% kubectl apply -f traefik.yml
serviceaccount/traefik-ingress-controller created
deployment.apps/traefik created
service/traefik created
Listing 7-3Traefik Installation via kubectl
至此,Traefik 已经安装并运行在我们的集群上。在 Kubernetes 的上下文中,安装应用和运行应用没有区别。它会在容器中自动启动。让我们看看清单 7-4 中的一些部署配置。
Many fields omitted for brevity
kind: Deployment
metadata:
name: traefik
spec:
replicas: 1
template:
spec:
containers:
- name: traefik
image: traefik:v2.2 #the Traefik Docker image used
args:
- --log.level=DEBUG
- --api.insecure
- --api.dashboard
- --entrypoints.web.address=:80
- --entrypoints.traefik.address=:8080
- --providers.kubernetescrd
ports:
- name: web
containerPort: 80
- name: traefik
containerPort: 8080
kind: Service
metadata:
name: traefik
spec:
type: NodePort
ports:
- protocol: TCP
port: 80
name: web
targetPort: 80
- protocol: TCP
port: 8080
name: traefik
targetPort: 8080
Listing 7-4Traefik Deployment Configuration
我们在清单 7-4 中列出了 Traefik 部署和服务的部分配置。您可以看到要部署的映像版本和副本的数量。Traefik 作为一个无状态服务运行,其状态由 Kubernetes 管理,因此根据需要运行多个副本进行扩展是没有问题的(因为它不必形成有状态的集群)。
有趣的部分是静态配置。动态 Traefik 配置全部由 Kubernetes 提供者在运行时提供,包括 Traefik 路由和 Traefik 服务。然而,静态配置——比如入口点和提供者——仍然需要在启动时提供。为此,Traefik 利用了常用的 CLI 参数方法。这是为 Docker 和 Kubernetes 传递 Traefik 静态配置的常用方式。我们从两个入口点开始:一个用于 HTTP 流量,另一个用于仪表板。我们还在不安全模式下公开仪表板,因为我们在本地运行,并将日志设置为所需的级别。这都是典型的东西。
新标志是--providers.kubernetescrd值。这确保了 Traefik 可以根据 Kubernetes CRDs 配置自己。微妙的是,它只选择新的基于 Traefik 的 CRD。如果您还希望 Traefik 充当标准的 Kubernetes 入口控制器,您必须传递--providers.kubernetesingress标志。您可以启用一个或两个提供程序。您还公开了 Traefik 端口本身,因此 Kubernetes 可以注册它们。不要忘记 Traefik 是作为 NodePort 类型的 Kubernetes 服务在集群外部公开的。服务如清单 7-5 所示。
% kubectl get svc traefik
NAME TYPE CLUSTER-IP PORT(S)
traefik NodePort 10.110.30.69 80:31624/TCP,8080:32529/TCP
Listing 7-5Traefik Kubernetes Service
不管 Traefik 运行在哪个 Kubernetes worker 节点上(本地只有一个), Kubernetes 都会将任何 worker 节点上公开的节点端口上的任何传入流量转发给正在运行的 Traefik 实例。如果有多个 Traefik pods,Kubernetes 会自动在它们之间对请求进行负载平衡。这是节点端口服务类型的常见 Kubernetes 行为。在云场景中,我们将使用 Traefik 服务的负载平衡器类型,它启动云负载平衡器来公开 Traefik 服务,所有对 Traefik 的请求都通过负载平衡器 IP。云平台运行负载均衡器。在本地,我们通常使用节点端口,尽管一些本地 Kubernetes 发行版现在也通过特殊机制支持负载平衡器。
现在让我们看看 Kubernetes 公开的端口 31624 上的默认 Traefik 后端(见图 7-1 )。此端口由 Kubernetes 随机分配,可能会有所不同。这会将请求转发到 HTTP 端口 80 上的 Traefik web 入口点。由于没有为这个入口点配置默认路由,我们照常得到 404。请注意,IP 地址是 minikube 虚拟机的本地 IP,可以使用minikube ip命令.访问
img/497627_1_En_7_Fig1_HTML.jpg
图 7-1
节点端口入口点上的默认后端
我们还暴露了另一个端口上的 Traefik 仪表板,以检查配置(参见图 7-2 )。配置了两个入口点:traefik和web。traefik入口点服务于仪表板路由,而web用于所有其他 HTTP 流量。请注意,Traefik 文档建议不要在实际生产使用中以不安全的模式公开仪表板。第四章讲述了如何通过 TLS 安全地暴露仪表板。对于本地使用,现在可以这样访问它。
img/497627_1_En_7_Fig2_HTML.jpg
图 7-2
使用仪表板入口点的流量部署
如果您向下滚动到 Providers 部分,您会看到 Kubernetes CRD 提供商已启用(参见图 7-3 )。在前面的章节中,我们使用了 FileProvider 和 Consul provider。旧的 Kubernetes 入口提供程序未启用,因此 Traefik 不作用于标准入口资源。
img/497627_1_En_7_Fig3_HTML.jpg
图 7-3
使用 kubernetes crd 提供程序进行 trafik 部署
接下来,我们在集群上部署一个 Kubernetes 服务,并尝试通过traefik入口点访问它。
安装 bookinfo 应用
现在 Traefik 已经启动并运行,让我们将一个微服务风格的应用部署到我们的集群中,它可以通过 Traefik 公开。为此,我们使用 Istio 文档中的 BookInfo 示例应用( https://istio.io/latest/docs/examples/bookinfo/ )。这个应用由几个不同的容器化微服务组成,虽然它主要是 Istio 服务网格的一个展示窗口,但我们可以将其用于 Traefik。
请注意,我们也可以使用上一章中的 Spring PetClinic 应用。然而,在撰写本文时,这个应用还不完全是 Kubernetes 的原生程序。我们必须做一些调整才能在 Kubernetes 上运行它。BookInfo 构建为运行在 Kubernetes 之上,因此我们可以直接关注部署后的部分。我们对它的实际代码不感兴趣。部署后,通过 Traefik 公开的唯一服务是主 web 应用,名为productpage。
web 应用在内部调用其余的服务。BookInfo 有许多不同的部署配置。我们只对基本部署感兴趣。这个文件在 https://github.com/istio/istio/blob/master/samples/bookinfo/platform/kube/bookinfo.yaml 。你可以下载它或者直接用原始网址应用它(参见清单 7-6 )。
% kubectl apply -f bookinfo.yml
service/details created
serviceaccount/bookinfo-details created
deployment.apps/details-v1 created
service/ratings created
serviceaccount/bookinfo-ratings created
deployment.apps/ratings-v1 created
service/reviews created
serviceaccount/bookinfo-reviews created
deployment.apps/reviews-v1 created
deployment.apps/reviews-v2 created
deployment.apps/reviews-v3 created
service/productpage created
serviceaccount/bookinfo-productpage created
deployment.apps/productpage-v1 created
Listing 7-6Install bookinfo Services
部署和服务启动后,我们可以继续应用 IngressRoute。这很标准。它只是公开了 web 应用需要从浏览器调用的所有路径。我们必须创建自己的来与 Traefik 合作。然后,我们可以在 HTTP web 节点端口上访问 BookInfo 应用。首先,让我们看看 BookInfo 的 Kubernetes 服务中有哪些是可用的(参见清单 7-7 )。
% kubectl get svc
NAME TYPE CLUSTER-IP PORT(S)
details ClusterIP 10.111.105.145 9080/TCP
productpage ClusterIP 10.101.6.99 9080/TCP
ratings ClusterIP 10.102.103.167 9080/TCP
reviews ClusterIP 10.103.10.20 9080/TCP
% kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE
3 separate reviews deployments, rest omitted for brevity
reviews-v1 1/1 1 1
reviews-v2 1/1 1 1
reviews-v3 1/1 1 1
Listing 7-7BookInfo Services
清单 7-7 是一组创建的 ClusterIP 类型的服务。这意味着它们可以相互通信,但在群集之外无法访问。我们必须使用 IngressRoute 公开的唯一服务是productpage服务。这是 BookInfo 的主要 web 应用,服务于端口 9080 上的请求。
我们可以为此服务应用一个 IngressRoute 自定义资源,以通过 Traefik 公开它。让我们首先来看看清单 7-8 中这个资源的一些配置。您可以看到它大体上匹配我们通常通过 FileProvider 定义的动态配置。定义了一个入口点,并使用通常的匹配规则进行路由。有趣的部分是后端服务配置通常所在的部分。在这里,您可以看到我们直接引用了一个 Kubernetes 服务并通过 Traefik 路由找到了它。因此,IngressRoute 或多或少定义了从入口点到后端 Kubernetes 服务的 Traefik 路由。如果需要,我们也可以在这里参考 Traefik 中间件。
spec:
entryPoints:
- web
routes:
- match: PathPrefix(
/productpage
) || PathPrefix(/static
) || Path(/login
) || Path(/logout
) || PathPrefix(/api/v1/products
)
kind: Rule
services:- name: productpage
port: 9080
- name: productpage
% kubectl apply -f bookinfo-product-ingress.yml
ingressroute.traefik.containo.us/bookinfo-productpage-ingress created
% kubectl get IngressRoute
NAME AGE
bookinfo-productpage-ingress 29s
Listing 7-8BookInfo IngressRoute
一旦服务在入口公开,我们就可以很容易地查看网页(见图 7-4 )。这在内部调用其他 BookInfo 服务来填充页面每个部分的数据。由于点评服务由三个不同的点评部署支持(如清单 7-7 所示),该网页在每个请求上看起来可能略有不同。
img/497627_1_En_7_Fig4_HTML.jpg
图 7-4
BookInfo 产品页面 UI
我们现在可以在 Traefik 仪表板中检查配置。您会看到路由器和服务配置显示出来。IngressRoute 已经自动创建了一个路由器和一个服务(Traefik 抽象,而不是 Kubernetes 抽象,尽管在本例中它们是相同的)。Kubernetes 服务由 Traefik 自动发现,并被视为 Traefik 中的服务抽象。您可以在如图 7-5 和 7-6 所示的仪表盘中看到路由器和服务信息。
img/497627_1_En_7_Fig6_HTML.jpg
图 7-6
产品页面入口服务
img/497627_1_En_7_Fig5_HTML.jpg
图 7-5
产品页面路由器
如果您浏览服务配置中的服务器(我们通常在这里定义后端服务的负载平衡器 IP),您会看到相应 Kubernetes 服务的 pod IPs(参见图 7-6 )。Traefik 可以自动检查来自 Kubernetes 的这些细节,并处理内部的 Kubernetes 网络(它需要这样做)。当 pod 发生任何变化时(例如,放大或缩小、pod 重新启动等。)时,Traefik 会自动选择它。这与上一章形成了有趣的对比。在那里,我们必须使用 Consul 提供者来跟踪服务,从而将 Traefik 与 Consul 服务注册中心集成起来。然而,Kubernetes 提供开箱即用的服务发现,Traefik 可以直接与 Kubernetes 对话以获取服务细节。
Note
此上下文中的 LoadBalancer 是指用于指定多个后端服务 IP 的 Traefik 配置,而不是 Kubernetes LoadBalancer。
如果还不清楚的话,这个例子说明了 IngressRoute 是如何以 CRD 风格实现 Traefik HTTP 路由器的。这是使用 CRD 方法优于普通入口的优势,它允许您更接近 Traefik 风格的配置。
安装带舵的 trafik
在上一节中,我们成功地在 Kubernetes 上部署了 Traefik,并使用它来公开服务。基本配置涉及一些不同的手动步骤。使用更高级的 Traefik 配置,所需的手动步骤可能会很快变得难以操作,特别是当我们希望随着时间的推移调整静态配置或定期升级 Traefik 时。对于生产安装,我们使用一个名为 Helm ( https://helm.sh/ )的包和发布管理器在 Kubernetes 上安装 Traefik。在 Helm 中,一个可部署的工件被捆绑到一个图表中,该图表封装了随着时间的推移安装和升级该软件所需的所有资源,包括配置和依赖关系。有一个 Traefik 舵图,我们从现在开始使用它来安装/升级 Traefik。Helm 重用由 kubectl 设置的当前 kubeconfig。我们已经安装了 Helm v3 的最新版本,并且我们的 kubeconfig 设置指向我们的本地 Kubernetes 集群。
中央 Helm 资源库(类似于 Maven central、PyPi 或 Debian apt 资源库)中可用的 Traefik 版本是较旧的 Traefik v1.7 版本。因此,在通过 Helm 安装 Traefik v2 之前,我们需要运行几个高级步骤(参见清单 7-9 )。这些注册了 Traefik v2 Helm 存储库,因此我们可以使用 Helm 安装 Traefik v2。确保您不会意外安装旧版本;它不支持 IngressRoute CRDs,并且工作方式有些不同。
% helm repo add traefik https://containous.github.io/traefik-helm-chart
"traefik" has been added to your repositories
% helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "traefik" chart repository
Update Complete. ☼ Happy Helming!☼
Listing 7-9Adding Traefik’s Helm repository
一旦完成,我们就可以使用 Traefik v2 的官方导航图安装它了。因为我们已经手动安装了 Traefik,所以我们需要重置整个集群(在 minikube 上很容易完成),或者删除到目前为止安装的所有东西。现在让我们进行全新安装。
探索 trafik 掌舵图
Traefik Helm chart 安装了将 Traefik 作为入口控制器运行所需的所有先决条件,包括 Kubernetes RBAC 配置和用于配置路由规则的 CRDs。除了一个例外,安装在本地或云 Kubernetes 上的方式是一样的。在托管云服务中,Traefik ingress 通过使用类型为 LoadBalancer 的 Kubernetes 服务向外界公开。这会在所有 Kubernetes 节点前自动启动托管云负载平衡器,并将所有传入流量路由至 Traefik 入口控制器。对于外部世界,单一入口点是云负载平衡器的 IP 或 DNS 名称。
裸机或本地虚拟机上不提供负载平衡器。Minikube 和 microk8s 可以通过一种特殊的机制来处理这个问题,尽管使用节点端口通常更简单。您需要确保您选择的 Kubernetes 发行版支持 LoadBalancer 类型的服务。否则,您必须将 Traefik 入口公开为 NodePort 类型的 Kubernetes 服务。这必须在舵安装期间通过定制舵图表值来解决。这可以通过在命令行中将它们传递给 Helm 来实现,或者更好的方法是为自定义值定义一个文件,该文件可以在安装过程中覆盖默认值。
让我们来看看舵图上的一些默认值( https://github.com/containous/traefik-helm-chart/blob/master/traefik/values.yaml )以及一些我们想要定制的值(参见清单 7-10 )。
Configure the deployment with number of pods
deployment:
replicas: 1
IngressRoute for the dashboard will be installed
ingressRoute:
dashboard:
enabled: true
Configure both types of dynamic Traefik providers
providers:
kubernetesCRD:
enabled: true
kubernetesIngress:
enabled: true
Configure ports
ports:
traefik:
port: 9000
# As recommended, the dashboard is not exposed by default in production
expose: false
The HTTP and HTTPS ports are opened by default
web:
port: 8000
expose: true
websecure:
port: 8443
expose: true
service:
enabled: true
type: LoadBalancer
rbac:
False value indicates Traefik can be used cluster-wide across all namespaces.
namespaced: false
Listing 7-10Some of the Default Values in Traefik Helm Chart values.yaml
这不是任何一种标准的库本内特人清单。这些只是应用于 Helm 图表中 Kubernetes YAMLs 的 YAML 格式的配置值。默认情况下,它具有以下配置。
trafik 的一个实例在 kubernetes pod 中运行
Traefik 服务通过负载平衡器公开。这可能不适用于非云集群。
旧的和新的动态配置 Kubernetes 提供者都被启用。因此 Traefik 监视标准入口和 Traefik 的自定义入口路由资源。
打开了三个入口点:一个名为 9000 上的traefik,另外两个用于 HTTP 和 HTTPS 流量。稍后我们将进一步探讨这个额外的入口点。
将自动创建仪表板的 IngressRoute 自定义资源。但是,仪表板入口点不会向 Traefik Kubernetes 服务公开。这有点不一致和混乱,因为 Traefik 建议在生产中为仪表板创建您自己的安全入口。但是,我们可以在本地利用这个 IngressRoute。这种行为在我们能找到的任何地方都没有记录,所以将来可能会改变。
掌舵图的 default values.yaml 里有很多这样的条目。我们鼓励您自行探索进一步的配置。大部分都与在 Kubernetes 上可靠地运行 Traefik 有关。例如,未启用 Traefik pods 在负载下的自动缩放;如果需要,可以打开它。请注意,在写这本书的时候,这些观察是为了舵图的当前状态。图表继续发展。
在我们定制这些值之前,我们可以使用helm template命令来查看默认生成的部署清单。这些非常类似于我们在前面几节中用来手动安装 Traefik 的内容。让我们运行命令,看看最终的配置是什么样的。由于产量很多,我们只关注其中的几块。鼓励您自己运行该命令来查看完整的输出(参见清单 7-11 )。
% helm template traefik traefik/traefik
Partial values in the output
Deployment configuration
#Kubernetes liveness probe
readinessProbe:
httpGet:
path: /ping
port: 9000
#Kubernetes liveness probe
livenessProbe:
httpGet:
path: /ping
port: 9000
# Traefik CLI arguments
args:
- "--entryPoints.traefik.address=:9000/tcp"
- "--entryPoints.web.address=:8000/tcp"
- "--entryPoints.websecure.address=:8443/tcp"
- "--api.dashboard=true"
- "--ping=true"
- "--providers.kubernetescrd"
- "--providers.kubernetesingress"
Service configuration
type: LoadBalancer
ports:
- port: 80
name: web
targetPort: "web"
protocol: "TCP" - port: 443
name: websecure
targetPort: "websecure"
protocol: "TCP"
IngressRoute
kind: IngressRoute
metadata:
name: traefik-dashboard
spec:
entryPoints:
- traefik
routes:
- match: PathPrefix(
/dashboard
) || PathPrefix(/api
)
kind: Rule
services:- name: api@internal
kind: TraefikService
- name: api@internal
Listing 7-11Generated Helm Template with Default Values
让我们来分析一下清单 7-11 中我们捕捉到了什么。静态配置通常通过 CLI 参数提供。我们已经提到了三个入口点和两个提供者。您还可以看到仪表板启用标志。您还会看到ping Traefik 服务已启用。我们稍后在仪表板中查看它;它暴露于/ping路径下的traefik端口。它为 Kubernetes 定期检查 Traefik pods 的健康状况提供了一种标准方法。如果健康探测失败,Kubernetes 会自动重启 Traefik pods。这提供了一种在 Kubernetes 上运行 Traefik 的弹性方法。
您会看到创建了一个 Ingres routetraefik-dashboard来公开集群外部的仪表板。这在默认安装中不起作用。原因是 Traefik 的服务配置中不包括traefik入口点端口 9000。虽然可以在集群内部访问它以进行运行状况检查,但是不能通过节点端口访问它,并且无法到达该入口点来访问控制面板。
要在安装/升级时自定义一些默认值,我们可以在文件中覆盖它们,并将其提供给 Helm CLI。让我们看看清单 7-12 中我们想要定制的值。虽然仪表板在生产中没有公开,但我们在这里覆盖了traefik入口点的配置,使仪表板在本地可用。我们将 Traefik 服务更改为 NodePort 类型,并将日志级别设置为INFO。如您所见,Traefik 静态配置仍然提供了 CLI 参数。舵轮图显示了一个additionalArguments特殊键来传递额外的参数。
additionalArguments:
- "--log.level=INFO"
ports:
traefik:
expose: true
service:
type: NodePort
Listing 7-12custom-values.yml for Traefik Helm Chart
让我们再次运行 Helm template 命令,查看生成的带有自定义值的部署清单。我们仅在清单 7-13 中列出新的/修改的值。这使用了我们在清单 7-12 中详述的同一个custom-values.yml文件。
% helm template --values=custom-values.yml traefik traefik/traefik
Partial changed values in the output
Deployment configuration
# Traefik CLI arguments
args:
- "--log.level=INFO"
Service configuration
type: NodePort
ports:
- port: 9000
name: traefik
targetPort: "traefik"
Listing 7-13Generated Helm Template with Custom Values
现在,我们可以使用此配置访问集群外部的 Traefik 控制面板。让我们继续安装 Traefik。
本地安装
在安装 Traefik 之前,还有一个问题是我们希望将 Traefik 安装在哪个 Kubernetes 名称空间中。在生产中,如果 Traefik 负责集群范围的入口问题,那么 kube-system 名称空间是一个很好的选择。它与其他集群管理/操作服务一起存在。另一种方法是将 Traefik 限制在特定的名称空间,并为集群的其余部分使用不同的入口控制器(或不同的 Traefik 部署)。舵轮图支持开箱即用的rbac.namespaced配置。对于我们的例子,我们坚持使用默认的名称空间。
现在让我们用 Helm 命令安装 Traefik(参见清单 7-14 )。我们重用我们在清单 7-12 中详述的同一个 custom-values.yml 文件。然后,我们运行几个命令来观察它是否被正确部署。由于仪表板端口 9000 现在暴露在节点端口上,您也可以打开仪表板(参见图 7-7 )。
img/497627_1_En_7_Fig7_HTML.jpg
图 7-7
安装舵的入口点
% helm install --values=custom-values.yml traefik traefik/traefik
NAME: traefik
LAST DEPLOYED: Wed Apr 22 00:26:27 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
% helm ls
NAME NAMESPACE REVISION STATUS CHART APP VERSION
traefik default 1 deployed traefik-8.9.1 2.2.5
% kubectl get pods
NAME READY STATUS RESTARTS AGE
traefik-5bcf58d556-vfhlv 1/1 Running 0 63s
% kubectl get svc traefik
NAME TYPE CLUSTER-IP PORT(S)
traefik NodePort 10.99.103.150 9000:31342/TCP,80:30085/TCP,443:30615/TCP
% kubectl get svc traefik -o yaml
spec:
clusterIP: 10.99.103.150
externalTrafficPolicy: Cluster
ports:
- name: traefik
nodePort: 31342
port: 9000
protocol: TCP
targetPort: traefik - name: web
nodePort: 30085
port: 80
protocol: TCP
targetPort: web - name: websecure
nodePort: 30615
port: 443
protocol: TCP
targetPort: websecure
selector:
app.kubernetes.io/instance: traefik
app.kubernetes.io/name: traefik
sessionAffinity: None
type: NodePort
% kubectl get IngressRoute traefik-dashboard -o yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
spec:
entryPoints:
- traefik
routes: - kind: Rule
match: PathPrefix(/dashboard
) || PathPrefix(/api
)
services:- kind: TraefikService
name: api@internal
- kind: TraefikService
Listing 7-14Install Traefik Using Helm
在图 7-7 中,你可以在仪表板上看到我们的三个入口点。您还可以深入到路由器,查看到 Traefik 内部仪表板和 ping 服务的路由映射。您可能会对路由器的名称感兴趣。对于ping@internal服务,一切如常。然而,指向api@internal的仪表板 IngressRoute 有一个生成的名称(见图 7-8 和 7-9 )。这是因为它有一个单独定义的 IngressRoute。所有定义的 IngressRoute 对象都使用类似的命名约定。
img/497627_1_En_7_Fig9_HTML.jpg
图 7-9
仪表板路线
img/497627_1_En_7_Fig8_HTML.jpg
图 7-8
安装头盔的路线
Helm chart 还安装了所有必需的 Traefik CRDs。Traefik 依靠标准的 Kubernetes 声明性配置机制,通过这些 CRD 来配置自己。每种动态路由器配置都有不同的 CRD。让我们看看清单 7-15 中安装了什么。
% kubectl get crd
NAME
ingressroutes.traefik.containo.us
ingressroutetcps.traefik.containo.us
ingressrouteudps.traefik.containo.us
middlewares.traefik.containo.us
tlsoptions.traefik.containo.us
tlsstores.traefik.containo.us
traefikservices.traefik.containo.us
Listing 7-15Traefik CRDs
公开 bookinfo 评论服务
在前面的小节中,我们观察了 Traefik 如何直接与 Kubernetes 服务对话,以在不同的 pods 或服务实例之间实现负载平衡。虽然很容易,但这不允许您利用我们在第三章中观察到的一些特殊流量路由功能,例如加权循环或镜像。这是通过使用另一个名为 TraefikService 的 CRD 在 Kubernetes 上实现的。既然 Traefik 已经启动并运行了 Helm,那么让我们使用 BookInfo over Traefik 来尝试一个稍微复杂一点的用例。继续使用上一节中详述的相同步骤将 BookInfo 再次部署到集群中(参见清单 7-6 )。一旦我们这样做了,我们应该在集群中再次拥有 BookInfo 资源。
我们现在在 Traefik ingress 上公开的服务是 reviews 服务。productpage后端内部调用它。这是一个单一的 Kubernetes 服务,由三个独立的部署提供支持。评论服务将流量路由到三个 pod,它们的行为略有不同。
reviews-v1返回样品评论。
reviews-v2调用评级服务并返回黑色样本评级。
reviews-v3调用评级服务并返回红色样本评级。
我们在使用 Traefikservice 的同时使用 IngressRoute 来公开此服务。这允许您尝试使用 Kubernetes 提供者的加权循环策略。服务启动后,我们可以继续应用 IngressRoute。首先,我们为三个单独的部署创建三个额外的 Kubernetes 服务(参见清单 7-16 )。Traefikservice 必须指向现有的 Kubernetes 服务。
apiVersion: v1
kind: Service
metadata:
name: reviews-noratings
labels:
app: reviews
version: v1
service: reviews
spec:
ports:
- port: 9080
name: http
selector:
app: reviews
version: v1
apiVersion: v1
kind: Service
metadata:
name: reviews-black
labels:
app: reviews
version: v2
service: reviews
spec:
ports:
- port: 9080
name: http
selector:
app: reviews
version: v2
apiVersion: v1
kind: Service
metadata:
name: reviews-red
labels:
app: reviews
version: v3
service: reviews
spec:
ports:
- port: 9080
name: http
selector:
app: reviews
version: v3
% kubectl apply -f bookinfo-reviews-extsvcs.yml
service/reviews-noratings created
service/reviews-black created
service/reviews-red created
We now have 4 reviews services, one from the original deployment
% kubectl get svc
NAME TYPE CLUSTER-IP PORT(S)
reviews ClusterIP 10.110.252.89 9080/TCP
reviews-black ClusterIP 10.97.126.88 9080/TCP
reviews-noratings ClusterIP 10.111.136.174 9080/TCP
reviews-red ClusterIP 10.105.94.3 9080/TCP
Listing 7-16Three Separate Reviews Services
一旦这些服务就位,让我们定义一个 Traefikservice 来对其中两个应用加权循环策略(参见清单 7-17 )。我们以 3:1 的权重将reviews-black和reviews-noratings服务组合在一起。
apiVersion: traefik.containo.us/v1alpha1
kind: TraefikService
metadata:
name: reviews-v1v2
spec:
weighted:
services:
- name: reviews-black
port: 9080
weight: 3
- name: reviews-noratings
port: 9080
weight: 1
% kubectl apply -f bookinfo-reviews-traefikservice.yml
traefikservice.traefik.containo.us/reviews-v1v2 created
Listing 7-173 TraefikService Resources
我们现在应用一个 IngressRoute 来对外公开这些服务(参见清单 7-18 )。
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: bookinfo-reviews-ingress
spec:
entryPoints:
- web
routes:
- match: PathPrefix(
/reviews
)
kind: Rule
services:We can define multiple services here for simple Round robin load balancing
- name: reviews-v1v2
kind: TraefikService - name: reviews-red
port: 9080
- name: reviews-v1v2
% kubectl apply -f bookinfo-review-ingress.yml
ingressroute.traefik.containo.us/bookinfo-reviw-ingress created
% kubectl get IngressRoute
NAME AGE
bookinfo-review-ingress 29s
Call reviews ingress on loop
% for ((i=1;i<=20;i++)); do curl http://192.168.64.5:30680/reviews/1 | jq ; done
Listing 7-18Reviews IngressRoute
如果您在一个循环中调用服务入口(参见清单 7-18 ,您会看到负载根据提供的权重分布在三个独立的实例中。我们得到的红色和黑色评论比没有评论的评论多。您可以在图 7-10 、 7-11 、 7-12 和 7-13 的仪表盘中查看这些服务和路由器。
img/497627_1_En_7_Fig13_HTML.jpg
图 7-13
用于入口服务的路由器
img/497627_1_En_7_Fig12_HTML.jpg
图 7-12
加权 Traefikservice
img/497627_1_En_7_Fig11_HTML.jpg
图 7-11
trafik 中的所有评论服务
img/497627_1_En_7_Fig10_HTML.jpg
图 7-10
生成的入口服务
用 Jaeger 配置请求跟踪
第五章展示了如何用 Zipkin 将 Traefik 集成到分布式跟踪设置中。虽然 Zipkin 几年来一直是 Spring Cloud 生态系统分布式追踪的默认选择,但在 Kubernetes 世界中,人们更喜欢使用 Jaeger ( www.jaegertracing.io )。Jaeger 是由优步发布的 CNCF 分布式追踪系统,它为运行在 Kubernetes 上的服务分发追踪。Jaeger 适用于大规模部署,支持 OpenTracing 和其他标准。其背后的基本概念与 Zipkin 非常相似。它将跟踪头传播到下游服务,并根据发送到收集器的所有数据聚合跨度。Jaeger 是 Traefik 的默认跟踪后端。如果我们启用请求跟踪,并且不指定任何其他内容,Traefik 会自动采用 Jaeger 跟踪。
在 Traefik 中进行 Jaeger 配置之前,我们需要一个正在运行的 Jaeger 实例。耶格的完整设置是广泛的,超出了本书的范围。(数字海洋云上的最小设置需要至少四个 Kubernetes 工作节点。)出于演示目的,Jaeger 提供了一个 AllInOne 映像,它将所有组件打包在一个可执行文件中,并使用内存存储来部署在一个单独的 pod 中。这需要部署一个耶格操作员和相关的 CRD。类似于 Traefik 的 CRD,当我们向 Kubernetes 提交一个类型为Jaeger的资源时,它会启动 AllInOne Jaeger pod。如果你想更深入地了解 minikube 上的 Jaeger 设置,我们鼓励你采用这种方法。
在我们的例子中,我们采用了一种更简单的方法来设置 Jaeger,这避免了前面提到的所有复杂性。我们从 minikube 切换到另一个本地 Kubernetes 发行版 microk8s。我们使用现有的头盔图在上面安装 Traefik,然后在 microk8s 上启用 Jaeger(参见清单 7-19 )。这将自动部署 jaeger-operator 并启动一个简单的 jaeger 一体化部署,无需我们进行任何手动操作。然后,我们可以确定在启动配置中配置哪个服务端点。我们只对特定服务的几个端口感兴趣:一个 TCP 和一个 UDP(参见清单 7-19 )。
% microk8s enable jaeger
% microk8s kubectl get pod
NAME READY STATUS
jaeger-operator-7b58b969cf-vh8pp 1/1 Running
simplest-658764ffff-xktbp 1/1 Running
% microk8s kubectl get svc simplest-agent
NAME TYPE PORT(S)
simplest-agent ClusterIP 5775/UDP,5778/TCP,6831/UDP,6832/UDP
Listing 7-19Enable Jaeger on microk8s
必须在启动时指定 Jaeger 特定配置以及请求跟踪配置。因为我们已经在集群上运行了 Traefik,所以我们可以调整配置并运行一个helm upgrade命令。这将启动一个启用跟踪的新 Traefik pod,并删除现有的一个。我们在 custom-values.yml 文件中添加了以下内容(参见清单 7-20 )。
additionalArguments:
New values in Helm configuration
- "--tracing=true"
- "--tracing.serviceName=traefik" # default value, can be omitted
- "--tracing.jaeger=true" # default value, can be omitted
- "--tracing.jaeger.samplingServerURL=http://simplest-agent:5778/sampling"
- "--tracing.jaeger.localAgentHostPort=simplest-agent:6831"
% helm upgrade --values=custom-values.yml traefik traefik/traefik
Listing 7-20Additions in custom-values.yml and Helm Upgrade
您可以查看我们的仪表板,并在图 7-14 中看到 Jaeger tracing 已启用。
img/497627_1_En_7_Fig14_HTML.jpg
图 7-14
耶格追踪已启用
以下是默认设置的 Jaeger 配置参数。
tracing . jaeger . sampling type = const
tracing . jaeger . sampling param = 1.0
默认情况下,这会将所有轨迹发送给 Jaeger。这可以通过调整来控制采样轨迹的数量。
tracing.jaeger.propagation = jaeger 耶格
这个可以改成发送 Zipkin 式的痕迹,耶格可以解读。
文档中还有一些其他配置参数。我们需要设置的两个必需参数已经在清单 7-20 中的 Helm 配置中。
由于跟踪是全局启用的,Traefik 现在开始向 Jaeger 收集器发送所有传入请求的跟踪。我们可以提出几个请求,然后检查 Jaeger UI(见图 7-15 )。
img/497627_1_En_7_Fig15_HTML.jpg
图 7-15
界面上的耶格痕迹
我们可以通过传入的serviceName配置来过滤跟踪,以隔离 Traefik 特定的跟踪,并进一步通过 Traefik 入口点来过滤。我们可以深入到特定的跟踪。图 7-16 中的是对 Traefik 仪表板的请求。
img/497627_1_En_7_Fig16_HTML.jpg
图 7-16
耶格追踪向下钻
这里还有一个有趣的地方。由于 Jaeger 实例运行在我们的集群中,我们如何访问 Jaeger UI 呢?它需要在节点端口上或通过 Traefik 入口(或另一个入口控制器)公开。由 microk8s 设置的 Jaeger 服务将其 UI 公开为集群中的默认入口规则。集群中有一个入口(不是 IngressRoute)资源,它是由 Jaeger 操作员自动创建的(参见清单 7-21 )。
% microk8s kubectl get ingress simplest-query -o yaml
kind: Ingress
spec:
backend:
serviceName: simplest-query
servicePort: 16686
Listing 7-21Jaeger UI Ingress on microk8s
您在本章的其他地方没有遇到过这种格式,因为您没有处理过纯 Kubernetes 入口资源。这为默认入口控制器定义了一个默认后端,所以如果我们点击根路径(/)上 Traefik 的 web/websecure 入口点,它会打开 Jaeger UI。您可以在 Traefik 仪表板中查看这一点,它已经为一个default-backend配置了一个default-router(参见图 7-17 )。这种行为有两个原因。
img/497627_1_En_7_Fig17_HTML.jpg
图 7-17
Jaeger 默认入口后端
Traefik Helm 图表设置了 Kubernetes CRD 供应商和普通 Kubernetes 供应商(如图 7-14 所示)。如果我们禁用了旧的提供程序,Traefik 不会注册它。
我们在此群集中没有其他入口控制器。默认的是 nginx-ingress-controller,但是我们没有启用它。Traefik 正在处理这次入侵。
虽然这不是我们想要的行为(这只是由于不可预见的变量,看起来不可定制,甚至没有记录),但这只是为了我们的示例。它允许您说明 Traefik 对 Kubernetes ingress 的支持,我们跳过了这一步。在任何真实的用例中,都不是这样设置的。正确的做法是通过 IngressRoute 对象来公开 Jaeger UI。
数字海洋立方体云上的设置交通
现在让我们在 DigitalOcean Kubernetes (DOKS)上设置 Traefik。任何希望使用不同云提供商的人都可以这样做(AKS、EKA、GKE 等)。这是一个由 DigitalOcean 管理的云 Kubernetes 集群。集群的实际配置超出了本书的范围。
DigitalOcean 通过点击操作使其变得非常简单。一旦启动,您将获得下载 kubeconfig 的说明,这样您就可以使用本地 Kubernetes CLI (kubectl)连接到您的集群。对于云,在 DOKS 上将 Traefik 服务公开为默认类型的负载平衡器会自动为我们提供一个云负载平衡器。我们现在只在清单 7-22 中定制 Helm 安装期间的日志级别。
从这里开始,kubeconfig 必须改为指向我们的云集群来执行所有命令。
kubeconfig has to be changed to point to cloud cluster for following commands
% helm install --set="additionalArguments={--log.level=INFO}" traefik traefik/traefik
% kubectl get svc traefik
NAME TYPE EXTERNAL-IP PORT(S)
traefik LoadBalancer 139.59.53.243 80:30415/TCP,443:32494/TCP
Listing 7-22Install Traefik Using Helm on Cloud LB
DigitalOcean 提供云负载平衡器。这个过程需要一点时间,之后我们获得公共外部 IP 来访问我们的入口点(参见清单 7-22 )。与 NodePort 不同,这里我们可以在默认端口上路由请求。回想一下,Traefik 会自动为 HTTPS 入口点生成一个自签名证书,因此您会看到如图 7-18 所示的端口 443。
img/497627_1_En_7_Fig18_HTML.jpg
图 7-18
云负载平衡器上具有自签名证书的 HTTPS 入口点
当我们继续时,我们得到 Traefik 的默认后端(见图 7-19 )。
img/497627_1_En_7_Fig19_HTML.jpg
图 7-19
云负载平衡器上的默认后端
我们没有公开这个部署的traefik端点,所以我们不能公开访问仪表板。Traefik 文档中建议这样做。现在有两种查看仪表板的方式。你可以做任何一个。
直接对仪表板端口执行kubectl port-forward命令。这将控制面板的访问权限限制为只有那些拥有 kubeconfig 的用户(如果集群内的服务直接连接到 pod,则可能还有集群内的服务)。
按照 Traefik 的建议,使用安全的 IngressRoute 暴露仪表板(类似于我们在第四章中所做的)。
Kubernetes 上的 TLS 终止通过让我们加密证书
现在您已经看到了在 Kubernetes 上运行 Traefik 所需的部分。唯一没有讨论的是 TLS 支持,这对于任何严肃的生产使用都是必要的。第四章介绍了一个公开的 Traefik 实例如何轻松地为 TLS 流量提供服务,以及如何对自动证书分发进行加密。
在第四章中,我们展示了在单个云虚拟机(或 droplet)上运行的 Traefik 上的安全路由,通过 TLS 连接(即websecure入口点)启用了基本身份验证。在本章中,我们尝试在 cloud Kubernetes 上使用 Traefik 做同样的事情。我们部署 BookInfo 服务,在websecure入口点上为它定义一个 IngressRoute,并从 Let's Encrypt 为该域请求一个有效的 TLS 证书。请记住,Let's Encrypt 只为可公开访问的域颁发有效证书。已颁发证书的域必须与您为有效 TLS 连接而访问的域的 URL 相匹配。为了用 Let's Encrypt 支持重新配置 Traefik,我们提供了一个新的 values 配置文件并升级了我们的 Helm 版本。我们还将日志级别更改为 DEBUG(参见清单 7-23 )。
additionalArguments:
- "--certificatesresolvers.letsencrypt.acme.email="
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.letsencrypt.acme.storage=/data/acme.json"
- "--certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--log.level=DEBUG"
persistence:
enabled: true
size: 1Gi #min. volume size allowed on DigitalOcean
storageClass: "do-block-storage"
Listing 7-23New cloud-values.yml file
我们在这里使用类似的让我们加密配置(见清单 7-23 ),我们上次在第四章中使用过,除了现在我们使用 CLI 参数而不是静态 YAML 配置。为了混淆,我们使用 HTTP-01 挑战,而不是 TLS-ALPN-01 挑战。这是一种更标准、更广泛使用的挑战类型。它要求 web 入口点(端口 80)是公共可达的(让我们加密)。
我们使用 Let's Encrypt 暂存 URL,因为我们不需要获得有效的生产证书。获取的证书的存储位置需要一些额外的配置。Traefik 的舵图提供了这一点。pod 通常是暂时的,如果没有 Kubernetes 持久卷的支持,就不能将数据持久化到 pod 文件系统中。
Kubernetes 存储抽象的详细讨论超出了我们的范围。Traefik Helm 图表为向文件系统写入数据提供了一个存储位置,该位置在 pod 重新启动后仍然存在。存储安装在位于/data位置的运行 Traefik pod 中,我们获得的证书保存在该文件夹中。否则,证书会在 pod 重新启动时丢失。作为参考,Helm 图表生成的存储配置如清单 7-24 所示。这可以像往常一样通过 Helm template 命令生成。它可以在舵图中进一步定制。存储类别do-block-storage属性是特定于供应商的。它需要在 DigitalOcean 中配置存储,在其他任何地方都没有用。
kind: PersistentVolumeClaim
metadata:
name: traefik
spec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: "1Gi"
storageClassName: "do-block-storage"
Additional Deployment configuration
spec:
template:
spec:
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: traefik
Listing 7-24Generated Storage Value Snippets
我们现在可以使用清单 7-25 中的文件来升级头盔以应用这个配置。
% helm upgrade --values=cloud-values.yml traefik traefik/traefik
Release "traefik" has been upgraded. Happy Helming!
NAME: traefik
LAST DEPLOYED: Sun Aug 16 20:34:16 2020
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None
Listing 7-25Helm Upgrade with certresolver
此时,我们可以使用与清单 7-6 中完全相同的配置来部署 BookInfo 服务。然后,我们定义一个新的 IngressRoute 来到达该服务。首先,我们打开另一个终端,并在清单 7-26 中跟踪 Traefik pod 日志。回想一下,我们将 Traefik 日志级别更改为 DEBUG。我们可以检查证书获取过程。
In a separate terminal
% kubectl get pod
NAME READY STATUS
traefik-6cb8d56bf8-sghpj 1/1 Running
% kubectl logs -f traefik-6cb8d56bf8-sghpj
time="2020-08-16T15:07:37Z" level=info msg="Configuration loaded from flags."
time="2020-08-16T15:07:37Z" level=info msg="Traefik version 2.2.8 built on 2020-07-28T15:46:03Z"
time="2020-08-16T15:07:37Z" level=debug msg="Static configuration loaded...
...
Listing 7-26Secure Dashboard IngressRoute
让我们让它继续运行,然后回到我们的主终端来应用 IngressRoute(参见清单 7-27 )。
whoami-doks-ingress.yml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: productpage-ingresstls
spec:
entryPoints:
- websecure
routes:
- match: Host(
k8straefik.rahulsharma.page
) && (PathPrefix(/productpage
) || PathPrefix(/static
) || Path(/login
) || Path(/logout
) || PathPrefix(/api/v1/products
))
kind: Rule
services:- name: productpage
port: 9080
tls:
certResolver: letsencrypt
- name: productpage
Apply the IngressRoute
% kubectl apply -f bookinfo-doks-ingress.yml
Listing 7-27TLS IngressRoute for bookinfo productpage
在清单 7-16 的 log tail 终端中,您会看到日志中关于应用 YAML 和打开浏览器的消息(参见清单 7-28 )。为了简洁起见,我们省略了许多信息。我们已经在公共域提供者中添加了一个子域条目,指向我们的负载平衡器的 IP 地址。您会看到 Traefik 首先尝试使用 TLS-ALPN-01 质询类型,然后退回到 HTTP-01 质询。这个挑战是一个多步骤的过程,它提供了对我们的公共可达域上的默认 HTTP 端口进行加密的自动响应,这就是为什么端口 80 需要为这个挑战开放的原因。
level=debug msg="Try to challenge certificate for domain [k8straefik.rahulsharma.page] found in HostSNI rule"
level=debug msg="Domains [\"k8straefik.rahulsharma.page\"] need ACME certificates generation for domains \"k8straefik.rahulsharma.page\"." providerName=letsencrypt.acme
level=debug msg="legolog: [INFO] [k8straefik.rahulsharma.page] acme: Could not find solver for: tls-alpn-01"
level=debug msg="legolog: [INFO] [k8straefik.rahulsharma.page] acme: use http-01 solver"
level=debug msg="legolog: [INFO] [k8straefik.rahulsharma.page] The server validated our request"
level=debug msg="legolog: [INFO] [k8straefik.rahulsharma.page] acme: Validations succeeded; requesting certificates"
level=debug msg="Certificates obtained for domains [k8straefik.rahulsharma.page]"
Listing 7-28Lets Encrypt HTTP-01 Certificate Negotiation Log Snippets
当您在浏览器中访问 BookInfo productpage URL 时,您会得到通常的响应,并且可以检查暂存证书(参见图 7-20 )。
img/497627_1_En_7_Fig20_HTML.jpg
图 7-20
具有 LE 证书的云负载平衡器上的 BookInfo 产品页面 UI
多个 trafik 实例的 tls 证书限制
你可能会觉得我们现在已经准备好推出 Traefik 来在 Kubernetes 上运行了;然而,有一个问题。我们还没有谈到的一点是高可用性(HA)。Kubernetes 的一个优点是,它通过在同一服务的多个单元之间负载平衡流量来自动提供 HA 支持。如果启用了自动伸缩,Kubernetes 将进一步横向扩展 pod 以处理请求的增加。
这也适用于 Traefik,因为它是作为部署支持的 Kubernetes 服务运行的。这个 HA 为 Traefik ACME 协议与 Let's Encrypt 的集成带来了问题。如您之前所见,自动化的“让我们加密 TLS”挑战需要多步互动。没有办法保证 Traefik 的同一个实例接收所有的质询请求。还记得 Traefik 将动态获取的证书存储在共享持久性卷中的 acme.json 文件中。
Traefik 文档警告您,该文件不应用于多个实例的并发访问。因此,当您水平扩展 Traefik 时,让我们加密集成会崩溃。Traefik 不允许这样的部署配置继续进行;它在舵安装期间抛出一个错误。
traefike 的商业版本 traefike 支持这种分布式加密配置。然而,如果我们想坚持使用免费社区版,这种方法是行不通的。我们可以将手动获取的证书配置为 Kubernetes 机密,然后在 IngressRoute 配置中引用它,如清单 7-29 所示。
whoami-doks-ingress.yml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: productpage-ingresstls
spec:
entryPoints:
- websecure
routes:
- match: Host(
k8straefik.rahulsharma.page
) && PathPrefix(/productpage
) || #etc..
kind: Rule
services:- name: productpage
port: 9080
tls:
secretName: k8straefik-tls # TLS certificate already added as Kubernetes Secret
- name: productpage
Listing 7-29TLS IngressRoute for Whoami
如果我们仍然希望使用 Let's Encrypt 来自动获取和更新证书,Traefik 建议使用 jet stack cert-manager(https://cert-manager.io)。Cert-manager 是 Kubernetes 上管理证书的实际解决方案。它使用与 Traefik 相同的 ACME 协议为不同的用例提供证书,尽管它不支持像 Traefik 那样的 TLS-ALPN-01 挑战。Cert-manager 拥有与 Kubernetes Ingress 的本机集成。如果我们创建一个普通的 Kubernetes 入口资源并添加正确的自定义注释,cert-manager 会自动为该域提供一个 TLS 证书。
问题是,在撰写本文时,它还不能与 Traefik 的 Ingres route CRD 一起使用。Traefik 团队正在进行这种整合。作为一种变通方法,现在,我们可以为 cert-manager 创建一个证书定制资源,它获取一个证书并将其保存为 Kubernetes 机密,然后管理其上的证书。这可以在 IngressRoute 资源中使用,如清单 7-29 所示。因为这是一个可能很快解决的限制的变通方法,所以我们不详细说明它。
摘要
本章使用其 Helm 图表在 Kubernetes 上部署和配置了 Traefik。Traefik 与 Kubernetes 紧密集成,其 API 网关功能可以轻松映射到 Kubernetes 入口。这使得它成为用作入口控制器的非常有吸引力的提议。
您以 CRDs 的形式尝试了 Traefik 与 Kubernetes 的本机集成。您看到了 Traefik 如何检测 Kubernetes API 服务器中的动态更新,并在没有任何人工干预的情况下保持其配置更新。服务在 Kubernetes 集群中更新时,会在 Traefik 中注册和注销。
虽然这一章在压缩的时间内涵盖了许多复杂的领域,但这种复杂性是 Kubernetes 生态系统所固有的。在我们看来,Traefik 简化了在 Kubernetes 上部署和管理 API 网关的工作。
就这样,我们来到了本章和本书的结尾。Traefik 每天都在快速发展,我们鼓励您阅读 Traefik 官方文档,继续您的 Traefik 探索之旅。