
王夕宁 阿里云高级技术专家,阿里云服务网格ASM技术负责人,关注Kubernetes、云原生、服务网格等领域。之前曾在IBM中国开发中心工作,曾担任专利技术评审委员会主席,作为架构师和主要开发人员负责或参与了一系列在SOA中间件、云计算等领域的工作,拥有50多项相关领域的国际技术专利。
作者: 王夕宁, 奇方 微服务提供了诸多价值,包括可伸缩性、敏捷性、独立扩展、业务逻辑隔离、独立生命周期管理和更容易的分布式开发。然而,这些分布众多的微服务也会增加安全的挑战, 每个微服务都是一个被攻击的目标。Kubernetes为托管和编排您的微服务提供了一个出色的平台。但是,默认情况下,微服务之间的所有交互都不安全。它们通过纯文本HTTP进行通信,但这不足以满足安全要求。只依赖网络边界来保证安全是不够的,因为一旦内部的某个服务被攻陷,边界安全手段就如马奇诺防线,攻击者能够以该机器为跳板来攻击内网。所以,内部的调用也必须安全,这就是零信任的用武之地。 零信任是Forrester分析师John Kindervag提出的, 是指无论在网络边界内部还是外部,都没有任何隐含的信任可言。 换句话说,任何地方都需要显式认证, 并使用最小权限原则来限制对资源的访问。 服务网格技术的一个重要的价值主张就是它如何有效地保护应用的生产环境,同时又不降低开发人员的生产力。通过服务网格技术, 为微服务架构采用零信任网络安全方法提供必要的基础, 以此实现所有访问都经过强身份验证、基于上下文授权、记录监控等安全目标。使用这些网格功能,您可以为属于网格的所有应用程序提供安全控制能力, 例如所有流量都已加密、到应用程序的所有流量都通过策略执行点(PEP)的验证等。 由美国国家安全局(NSA)于 2021 年 8 月发布的《Kubernetes Hardening Guidance》也提到了管理员应该考虑使用服务网格来加强 Kubernetes 集群的安全性。 阿里云服务网格ASM(https://www.aliyun.com/product/servicemesh)成为重要的云原生零信任体系落地载体之一, 将身份验证和授权从应用程序代码卸载到服务网格, 开箱即用、动态可配、更新策略更加容易且立即生效。在使用Kubernetes Network Policy实现三层网络安全控制之上, 服务网格ASM提供了包括对等身份和请求身份认证能力、Istio 授权策略以及更为精细化管理的基于OPA(Open Policy Agent)的策略控制能力。阿里云服务网格ASM提供的这些零信任安全能力, 帮助用户实现上述这些安全目标。 构建服务网格ASM产品能力的理论体系包括了以下几个方面: 1)零信任的基础 - 工作负载身份, 如何为云原生工作负载提供统一的身份; ASM产品为服务网格下的每一个工作负载提供了简单易用的身份定义, 并根据特定场景提供定制机制用于扩展身份构建体系, 同时兼容社区SPIFFE标准;2)零信任的载体 - 安全证书, ASM产品提供了如何签发证书以及管理证书的生命周期、轮转等机制, 通过X509 TLS证书建立身份,每个代理都使用该证书。并提供证书和私钥轮换; 3) 零信任的引擎 - 策略执行, 基于策略的信任引擎是构建零信任的关键核心, ASM产品除了支持Istio RBAC授权策略之外, 还提供了基于OPA提供更加细粒度的授权策略;4)零信任的洞察 - 可视化与分析, ASM产品提供了可观测机制用于监视策略执行的日志和指标, 来判断每一个策略的执行情况等; 为什么要使用服务网格实现零信任? 与直接在应用程序代码中构建这些安全机制的传统方法相比,服务网格体系结构具有以下多种安全性好处: Sidecar代理的生命周期与应用程序保持独立,因此可以更轻松地管理这些Sidecar代理。允许动态配置, 更新策略变得更加容易,更新立即生效,而无需重新部署应用程序。 服务网格的集中控制架构使企业的安全团队可以构建、管理和部署适用于整个企业的安全策略,从而默认情况下就能确保应用开发人员构建的业务应用的安全。他们无需额外的工作即可立即使用这些安全策略。服务网格提供了对附加到请求的终端用户凭据进行身份验证的能力如JWT。 此外, 使用服务网格体系结构,可以将身份验证和授权系统作为服务部署在网格中。这样一来, 如同网格中的其他服务一样,这些安全系统从网格中本身也可以得到安全保证, 包括传输中的加密、身份识别、策略执行点、终端用户凭据的身份验证和授权等。 借助阿里云服务网格ASM,可以使用单个控制平面来实施强大的身份和访问管理,透明的TLS和加密,身份验证和授权以及审核日志记录。阿里云服务网格ASM开箱即用地提供了这些功能,并且安装和管理它的简单性使开发人员,系统管理员和安全团队可以适当地保护其微服务应用程序。 ASM中的零信任体系 服务网格能够减小云原生环境中的被攻击面积,并提供零信任应用网络所需的基础框架。通过ASM管理服务到服务的安全性,可以确保服务网格的端到端加密、服务级别身份认证和细粒度授权策略。 在服务网格体系下, 可以支持: 在服务之间实施双向 TLS认证或者面向server侧的TLS认证,支持证书的自动轮转等生命周期管理。网格内的通信都经过身份验证和加密处理。启用基于身份的细粒度授权,以及基于其他维度参数的授权。基于角色访问控制 (RBAC) 的基础,支持“最低权限”的立场,也就是只有经过授权的服务才能根据 ALLOW/DENY 规则相互通信。 当前, 阿里云服务网格ASM提供了如下的零信任安全基本能力: 1. 工作负载身份 当应用程序在服务网格环境中运行时,服务网格会为每个服务提供一个唯一标识。连接到服务网格中运行的其他微服务时,将会使用该标识。服务标识可用于服务的双向验证,以此进行验证是否允许服务间的访问, 同时该服务标识也可用于授权策略中。 当使用服务网格ASM管理运行在Kubernetes上的工作负载或者基于WorkloadEntry定义虚拟机工作负载时,ASM会为每个工作负载提供服务身份;该身份基于工作负载的服务帐户令牌实现。 ASM中的服务身份符合SPIFFE标准,并具有以下格式: spiffe://<trust-domain>/ns/<namespace>/sa/<service-account> 在服务网格ASM控制台上, 打开对应的ASM实例, 在左侧导航栏中可以看到如下零信任安全下的工作负载身份。 数据面中的Kubernetes集群下的工作负载及其身份定义: 基于WorkloadEntry定义虚拟机工作负载及其身份定义: 2. 对等身份认证 认证指的是身份:这个服务是谁?这个最终用户是谁?我可以相信他们就是他们所说的那样吗? ASM产品提供了两种身份验证: - 对等身份验证 - 当两个微服务相互交互时,启用还是禁用双向TLS进行对等身份验证。 - 请求身份验证 - 允许最终用户和系统使用请求身份认证与微服务进行交互。通常使用JSON Web令牌(JWT)执行该操作。 按照入门指引(https://help.aliyun.com/document_detail/149552.html)安装部署bookinfo示例。 首先,当尝试从同一命名空间(例如本示例中为default)中的productpage pod 使用纯 HTTP 访问details服务时,默认情况下请求应该以 status 200 成功返回。这是因为在默认情况下,TLS和纯文本流量都可以被接受。 kubectl exec $(kubectl get pod -l app=productpage -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl http://details:9080/details/1 -o /dev/null -s -w '%{http_code}\n' 接着, 在命名空间default下定义对等身份认证。 在服务网格ASM控制台上, 打开对应的ASM实例, 在左侧导航栏中可以看到如下零信任安全下的对等身份认证。在右侧页面中, 点击“新建双向mTLS模式”按钮, 为工作负载details定义mTLS模式为STRICT。 再次, 使用productpage pod 使用纯HTTP访问details服务时: kubectl exec $(kubectl get pod -l app=productpage -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl http://details:9080/details/1 -o /dev/null -s -w '%{http_code}\n' 000 command terminated with exit code 56 退出码56 表示接收网络数据失败。这是符合预期的, 工作负载details定义了mTLS模式为STRICT, 这样在每个请求中都需要TLS证书认证。 为了允许正常访问, 可以通过上述定义的对等身份认证从STRICT修改为PERMISSIVE。对应的YAML定义如下: apiVersion: security.istio.io/v1beta1 kind: PeerAuthentication metadata: name: details-strict namespace: default spec: mtls: mode: PERMISSIVE selector: matchLabels: app: details 3. 请求身份验证 首先,我们将创建一个请求身份认证策略来对details服务的入站请求强制执行JWT身份验证。在服务网格ASM控制台上, 打开对应的ASM实例, 在左侧导航栏中可以看到如下零信任安全下的请求身份认证。在右侧页面中, 点击“新建”按钮, 为工作负载details定义jwt规则。 其中, issuer值设置为"testing@secure.istio.io", jwks的值取自https://raw.githubusercontent.com/istio/istio/release-1.9/security/tools/jwt/samples/jwks.json,对应如下: { "keys":[ {"e":"AQAB","kid":"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ","kty":"RSA","n":"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVuoeJT_DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_-4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u-YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ"}]} 接着, 尝试使用productpage pod 使用纯 HTTP访问details服务时,可以看到返回结果为200。 其中设置变量TOKEN值为: export TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg kubectl exec $(kubectl get pod -l app=productpage -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl http://details:9080/details/1 -o /dev/null --header "Authorization: Bearer $TOKEN" -s -w '%{http_code}\n' 200 如果传递的是无效令牌,我们应该看到“401: Unauthorized”响应: kubectl exec $(kubectl get pod -l app=productpage -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl http://details:9080/details/1 -o /dev/null --header "Authorization: Bearer badtoken" -s -w '%{http_code}\n' 401 但是,如果我们根本不传递令牌,RequestAuthentication则不会调用该策略。不使用JWT Token的请求, 同样也返回了200。 kubectl exec $(kubectl get pod -l app=productpage -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl http://details:9080/details/1 -o /dev/null -s -w '%{http_code}\n' 200 因此,除了此身份验证策略之外,我们还需要一个授权策略,该策略要求对所有请求进行 JWT。下一部分会讲述如何在ASM产品中定义授权策略。 4. 授权策略 ASM产品提供了授权策略, 可以使用授权策略AuthorizationPolicy资源来激活微服务之间的授权机制,并使用以下内容建立适当的流量授权策略机制: 工作负载标签选择selector字段指定策略目标;action 字段指定是 ALLOW 还是 DENY 请求。如果您未指定操作,则操作默认设置为 ALLOW。为清晰起见,我们建议您始终指定操作。(授权政策还支持 AUDIT 和 CUSTOM 操作); rules 指定何时触发操作: rules 中的 from 字段指定请求的来源;rules 中的 to 字段指定请求的操作; when 字段指定为了应用规则所需满足的其他条件; 对应的YAML定义如下: apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: require-jwt namespace: default spec: action: ALLOW rules: - from: - source: requestPrincipals: - testing@secure.istio.io/testing@secure.istio.io selector: matchLabels: app: details 再次不使用JWT Token发送请求, 您现在应该看到403 - Forbidden。这就是AuthorizationPolicy生效了,所有前端请求都必须有一个 JWT Token。 kubectl exec $(kubectl get pod -l app=productpage -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl http://details:9080/details/1 -o /dev/null -s -w '%{http_code}\n' 403 5. OPA策略 作为由CNCF托管的一个孵化项目,开放策略代理(OPA)是一个策略引擎,可用于为您的应用程序实现细粒度的访问控制。OPA作为通用策略引擎,可以与微服务一起部署为独立服务。为了保护应用程序,必须先授权对微服务的每个请求,然后才能对其进行处理。为了检查授权,微服务对OPA进行API调用,以确定请求是否被授权。 服务网格ASM集成了开放策略代理(OPA)插件,通过OPA定义访问控制策略,可以使您的应用实现细粒度的访问控制,并支持动态更新OPA策略。 具体可以参考https://help.aliyun.com/document_detail/277428.html 总结及参考案例 综上所示, 服务网格ASM提供了以下用于增强安全性的组件: 提供具有完整证书生命周期管理的托管证书基础设施, 解决了证书颁发和 CA 轮换的复杂性;托管的控制面API,用于向Envoy代理分发身份验证策略,授权策略和安全命名信息; Sidecar代理通过提供策略执行点PEP来帮助确保网格的安全;Envoy代理扩展允许遥测数据收集和审计; 每一个工作负载通过X509 TLS证书建立身份,每个Sidecar代理都使用该证书。服务网格ASM会提供并定期轮换证书和私钥。如果某个特定的私钥被盗用,服务网格很快就会用新的私钥替换它,从而大大减少了攻击面。 参考案例 使用授权策略在入口网关上实施基于 IP 的访问控制(参考文档 https://help.aliyun.com/document_detail/214764.html?spm=a2c4g.11186623.6.613.6b213918q0rlZV#title-f15-wyq-fxg)、或者基于自定义外部授权的访问控制等, 如下图所示某云产品基于ASM的网关授权策略实现。 某互联金融客户在解决跨集群多语言应用的访问权限控制方面, 使用阿里云服务网格ASM提供的授权策略隔离外联区域和应用区域。同时可以结合出口网关来审计出网格的流量,配合上授权策略,还可以控制应用对第三方服务的访问权限。
作者: 王夕宁, 奇方 微服务提供了诸多价值,包括可伸缩性、敏捷性、独立扩展、业务逻辑隔离、独立生命周期管理和更容易的分布式开发。然而,这些分布众多的微服务也会增加安全的挑战, 每个微服务都是一个被攻击的目标。Kubernetes为托管和编排您的微服务提供了一个出色的平台。但是,默认情况下,微服务之间的所有交互都不安全。它们通过纯文本HTTP进行通信,但这不足以满足安全要求。只依赖网络边界来保证安全是不够的,因为一旦内部的某个服务被攻陷,边界安全手段就如马奇诺防线,攻击者能够以该机器为跳板来攻击内网。所以,内部的调用也必须安全,这就是零信任的用武之地。 零信任是Forrester分析师John Kindervag提出的, 是指无论在网络边界内部还是外部,都没有任何隐含的信任可言。 换句话说,任何地方都需要显式认证, 并使用最小权限原则来限制对资源的访问。 服务网格技术的一个重要的价值主张就是它如何有效地保护应用的生产环境,同时又不降低开发人员的生产力。通过服务网格技术, 为微服务架构采用零信任网络安全方法提供必要的基础, 以此实现所有访问都经过强身份验证、基于上下文授权、记录监控等安全目标。使用这些网格功能,您可以为属于网格的所有应用程序提供安全控制能力, 例如所有流量都已加密、到应用程序的所有流量都通过策略执行点(PEP)的验证等。 由美国国家安全局(NSA)于 2021 年 8 月发布的《Kubernetes Hardening Guidance》也提到了管理员应该考虑使用服务网格来加强 Kubernetes 集群的安全性。 阿里云服务网格ASM(https://www.aliyun.com/product/servicemesh)成为重要的云原生零信任体系落地载体之一, 将身份验证和授权从应用程序代码卸载到服务网格, 开箱即用、动态可配、更新策略更加容易且立即生效。在使用Kubernetes Network Policy实现三层网络安全控制之上, 服务网格ASM提供了包括对等身份和请求身份认证能力、Istio 授权策略以及更为精细化管理的基于OPA(Open Policy Agent)的策略控制能力。阿里云服务网格ASM提供的这些零信任安全能力, 帮助用户实现上述这些安全目标。 构建服务网格ASM产品能力的理论体系包括了以下几个方面:1)零信任的基础 - 工作负载身份, 如何为云原生工作负载提供统一的身份; ASM产品为服务网格下的每一个工作负载提供了简单易用的身份定义, 并根据特定场景提供定制机制用于扩展身份构建体系, 同时兼容社区SPIFFE标准;2)零信任的载体 - 安全证书, ASM产品提供了如何签发证书以及管理证书的生命周期、轮转等机制, 通过X509 TLS证书建立身份,每个代理都使用该证书。并提供证书和私钥轮换;3) 零信任的引擎 - 策略执行, 基于策略的信任引擎是构建零信任的关键核心, ASM产品除了支持Istio RBAC授权策略之外, 还提供了基于OPA提供更加细粒度的授权策略;4)零信任的洞察 - 可视化与分析, ASM产品提供了可观测机制用于监视策略执行的日志和指标, 来判断每一个策略的执行情况等; 为什么要使用服务网格实现零信任?与直接在应用程序代码中构建这些安全机制的传统方法相比,服务网格体系结构具有以下多种安全性好处:Sidecar代理的生命周期与应用程序保持独立,因此可以更轻松地管理这些Sidecar代理。允许动态配置, 更新策略变得更加容易,更新立即生效,而无需重新部署应用程序。服务网格的集中控制架构使企业的安全团队可以构建、管理和部署适用于整个企业的安全策略,从而默认情况下就能确保应用开发人员构建的业务应用的安全。他们无需额外的工作即可立即使用这些安全策略。服务网格提供了对附加到请求的终端用户凭据进行身份验证的能力如JWT。此外, 使用服务网格体系结构,可以将身份验证和授权系统作为服务部署在网格中。这样一来, 如同网格中的其他服务一样,这些安全系统从网格中本身也可以得到安全保证, 包括传输中的加密、身份识别、策略执行点、终端用户凭据的身份验证和授权等。 借助阿里云服务网格ASM,可以使用单个控制平面来实施强大的身份和访问管理,透明的TLS和加密,身份验证和授权以及审核日志记录。阿里云服务网格ASM开箱即用地提供了这些功能,并且安装和管理它的简单性使开发人员,系统管理员和安全团队可以适当地保护其微服务应用程序。 ASM中的零信任体系服务网格能够减小云原生环境中的被攻击面积,并提供零信任应用网络所需的基础框架。通过ASM管理服务到服务的安全性,可以确保服务网格的端到端加密、服务级别身份认证和细粒度授权策略。 在服务网格体系下, 可以支持:在服务之间实施双向 TLS认证或者面向server侧的TLS认证,支持证书的自动轮转等生命周期管理。网格内的通信都经过身份验证和加密处理。启用基于身份的细粒度授权,以及基于其他维度参数的授权。基于角色访问控制 (RBAC) 的基础,支持“最低权限”的立场,也就是只有经过授权的服务才能根据 ALLOW/DENY 规则相互通信。 当前, 阿里云服务网格ASM提供了如下的零信任安全基本能力:1. 工作负载身份 当应用程序在服务网格环境中运行时,服务网格会为每个服务提供一个唯一标识。连接到服务网格中运行的其他微服务时,将会使用该标识。服务标识可用于服务的双向验证,以此进行验证是否允许服务间的访问, 同时该服务标识也可用于授权策略中。 当使用服务网格ASM管理运行在Kubernetes上的工作负载或者基于WorkloadEntry定义虚拟机工作负载时,ASM会为每个工作负载提供服务身份;该身份基于工作负载的服务帐户令牌实现。 ASM中的服务身份符合SPIFFE标准,并具有以下格式:spiffe://<trust-domain>/ns/<namespace>/sa/<service-account> 在服务网格ASM控制台上, 打开对应的ASM实例, 在左侧导航栏中可以看到如下零信任安全下的工作负载身份。 数据面中的Kubernetes集群下的工作负载及其身份定义: 基于WorkloadEntry定义虚拟机工作负载及其身份定义: 2. 对等身份认证认证指的是身份:这个服务是谁?这个最终用户是谁?我可以相信他们就是他们所说的那样吗?ASM产品提供了两种身份验证:- 对等身份验证 - 当两个微服务相互交互时,启用还是禁用双向TLS进行对等身份验证。- 请求身份验证 - 允许最终用户和系统使用请求身份认证与微服务进行交互。通常使用JSON Web令牌(JWT)执行该操作。 按照入门指引(https://help.aliyun.com/document_detail/149552.html)安装部署bookinfo示例。 首先,当尝试从同一命名空间(例如本示例中为default)中的productpage pod 使用纯 HTTP 访问details服务时,默认情况下请求应该以 status 200 成功返回。这是因为在默认情况下,TLS和纯文本流量都可以被接受。kubectl exec $(kubectl get pod -l app=productpage -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl http://details:9080/details/1 -o /dev/null -s -w '%{http_code}\n'接着, 在命名空间default下定义对等身份认证。在服务网格ASM控制台上, 打开对应的ASM实例, 在左侧导航栏中可以看到如下零信任安全下的对等身份认证。在右侧页面中, 点击“新建双向mTLS模式”按钮, 为工作负载details定义mTLS模式为STRICT。 再次, 使用productpage pod 使用纯HTTP访问details服务时:kubectl exec $(kubectl get pod -l app=productpage -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl http://details:9080/details/1 -o /dev/null -s -w '%{http_code}\n' 000 command terminated with exit code 56 退出码56 表示接收网络数据失败。这是符合预期的, 工作负载details定义了mTLS模式为STRICT, 这样在每个请求中都需要TLS证书认证。 为了允许正常访问, 可以通过上述定义的对等身份认证从STRICT修改为PERMISSIVE。对应的YAML定义如下:apiVersion: security.istio.io/v1beta1 kind: PeerAuthentication metadata: name: details-strict namespace: default spec: mtls: mode: PERMISSIVE selector: matchLabels: app: details 3. 请求身份验证 首先,我们将创建一个请求身份认证策略来对details服务的入站请求强制执行JWT身份验证。在服务网格ASM控制台上, 打开对应的ASM实例, 在左侧导航栏中可以看到如下零信任安全下的请求身份认证。在右侧页面中, 点击“新建”按钮, 为工作负载details定义jwt规则。 其中, issuer值设置为"testing@secure.istio.io",jwks的值取自https://raw.githubusercontent.com/istio/istio/release-1.9/security/tools/jwt/samples/jwks.json,对应如下: { "keys":[ {"e":"AQAB","kid":"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ","kty":"RSA","n":"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVuoeJT_DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_-4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u-YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ"}]} 接着, 尝试使用productpage pod 使用纯 HTTP访问details服务时,可以看到返回结果为200。 其中设置变量TOKEN值为:export TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg kubectl exec $(kubectl get pod -l app=productpage -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl http://details:9080/details/1 -o /dev/null --header "Authorization: Bearer $TOKEN" -s -w '%{http_code}\n' 200 如果传递的是无效令牌,我们应该看到“401: Unauthorized”响应:kubectl exec $(kubectl get pod -l app=productpage -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl http://details:9080/details/1 -o /dev/null --header "Authorization: Bearer badtoken" -s -w '%{http_code}\n' 401 但是,如果我们根本不传递令牌,RequestAuthentication则不会调用该策略。不使用JWT Token的请求, 同样也返回了200。kubectl exec $(kubectl get pod -l app=productpage -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl http://details:9080/details/1 -o /dev/null -s -w '%{http_code}\n' 200 因此,除了此身份验证策略之外,我们还需要一个授权策略,该策略要求对所有请求进行 JWT。下一部分会讲述如何在ASM产品中定义授权策略。 4. 授权策略ASM产品提供了授权策略, 可以使用授权策略AuthorizationPolicy资源来激活微服务之间的授权机制,并使用以下内容建立适当的流量授权策略机制:工作负载标签选择selector字段指定策略目标;action 字段指定是 ALLOW 还是 DENY 请求。如果您未指定操作,则操作默认设置为 ALLOW。为清晰起见,我们建议您始终指定操作。(授权政策还支持 AUDIT 和 CUSTOM 操作);rules 指定何时触发操作:rules 中的 from 字段指定请求的来源;rules 中的 to 字段指定请求的操作;when 字段指定为了应用规则所需满足的其他条件; 对应的YAML定义如下:apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: require-jwt namespace: default spec: action: ALLOW rules: - from: - source: requestPrincipals: - testing@secure.istio.io/testing@secure.istio.io selector: matchLabels: app: details 再次不使用JWT Token发送请求, 您现在应该看到403 - Forbidden。这就是AuthorizationPolicy生效了,所有前端请求都必须有一个 JWT Token。 kubectl exec $(kubectl get pod -l app=productpage -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl http://details:9080/details/1 -o /dev/null -s -w '%{http_code}\n' 403 5. OPA策略作为由CNCF托管的一个孵化项目,开放策略代理(OPA)是一个策略引擎,可用于为您的应用程序实现细粒度的访问控制。OPA作为通用策略引擎,可以与微服务一起部署为独立服务。为了保护应用程序,必须先授权对微服务的每个请求,然后才能对其进行处理。为了检查授权,微服务对OPA进行API调用,以确定请求是否被授权。 服务网格ASM集成了开放策略代理(OPA)插件,通过OPA定义访问控制策略,可以使您的应用实现细粒度的访问控制,并支持动态更新OPA策略。具体可以参考https://help.aliyun.com/document_detail/277428.html 总结及参考案例综上所示, 服务网格ASM提供了以下用于增强安全性的组件: 提供具有完整证书生命周期管理的托管证书基础设施, 解决了证书颁发和 CA 轮换的复杂性;托管的控制面API,用于向Envoy代理分发身份验证策略,授权策略和安全命名信息;Sidecar代理通过提供策略执行点PEP来帮助确保网格的安全;Envoy代理扩展允许遥测数据收集和审计; 每一个工作负载通过X509 TLS证书建立身份,每个Sidecar代理都使用该证书。服务网格ASM会提供并定期轮换证书和私钥。如果某个特定的私钥被盗用,服务网格很快就会用新的私钥替换它,从而大大减少了攻击面。 参考案例使用授权策略在入口网关上实施基于 IP 的访问控制(参考文档 https://help.aliyun.com/document_detail/214764.html?spm=a2c4g.11186623.6.613.6b213918q0rlZV#title-f15-wyq-fxg)、或者基于自定义外部授权的访问控制等, 如下图所示某云产品基于ASM的网关授权策略实现。 某互联金融客户在解决跨集群多语言应用的访问权限控制方面, 使用阿里云服务网格ASM提供的授权策略隔离外联区域和应用区域。同时可以结合出口网关来审计出网格的流量,配合上授权策略,还可以控制应用对第三方服务的访问权限。
服务网格本质上是存在于服务之间的网络,为微服务带来了弹性,。通过一种轻量级的Sidecar模式负责管理服务之间的流量, 可用于部署具有弹性的微服务。容错能力是指系统在部分故障期间, 仍然能够继续运行的能力。创建一个可靠的弹性系统会对其中的所有服务提出容错要求。云环境的动态性质要求编写的服务能预见这些失败, 并能优雅地响应意外情况。服务网格在不要求应用程序修改任何代码的情况下为应用程序带来了容错能力。在分布式系统架构下, 必须考虑面向失败的设计。 任何一个服务都必须假定远程调用可能失败,并且必须使用适当的后备操作进行准备。一个服务中断可能会引起连锁反应从而导致严重的业务后果,因此我们构建、运行和测试系统的弹性能力非常必要。使用服务网格技术构建的容错解决方案通常着重于以下几个方面:超时处理(timeouts)隔板模式(bulkheads)回退机制(fallbacks)熔断机制(circuit breakers)超时处理防止故障的第一道防线就是使用超时机制。通过设置超时, 可以确保应用程序在后端服务无响应时可以收到错误返回,从而使其能够以适当的回退行为进行处理。超时更改的是发出请求的客户端等待响应的时间, 它们对目标服务的处理行为没有影响,因此这并不意味着请求的操作失败。服务网格ASM支持在VirtualService定义中为路由设置超时策略来更改超时值,如果sidecar代理在设置值时间内未收到响应,则该请求将失败。该策略如下所示:apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: httpbin spec: hosts: - 'httpbin' http: - route: - destination: host: httpbin timeout: 5s通过这种方式调整超时,所有使用路由的请求都将使用该超时设置。隔板模式隔板模式有助于隔离用于服务的资源,并避免级联故障。通过以下使用DestinationRule来限制上游服务的连接池,从而定义服务间隔板。 其中,最大连接数和连接超时时间是对 TCP 和 HTTP 都有效的通用连接设置; 而每个连接最大请求数和最大请求重试次数仅针对 HTTP1.1/HTTP2/GRPC 协议的连接生效。示例如下:apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: httpbin spec: host: httpbin trafficPolicy: connectionPool: http: http1MaxPendingRequests: 1 maxRequestsPerConnection: 1 tcp: connectTimeout: 10s maxConnections: 1此配置将与示例服务的每个实例建立的最大并发连接数限制为1, 并且不能在10秒内建立连接的服务会得到503 -- Service Unavailable 响应。重试机制在微服务弹性设计架构中,假设请求某个服务节点时遭遇请求失败,例如请求超时、连接超时、服务宕机等一系列可能的错误,这时可以通过配置重试机制,重新请求其他服务。重试请求虽然是最简单的回退机制, 但重试请求可能会导致级联的系统故障, 也就是常说的重试风暴。不要过于频繁地重试或重试过长时间。 在短时间内重试不太可能成功,因为服务可能还未恢复。 此外,如果在服务尝试恢复时进行了大量连接尝试,则其可能会承受更大压力,并且反复的连接尝试甚至可能使服务不堪重负,导致潜在问题变得更加严重。服务网格技术可以更有效地执行重试。它已经直接参与请求路由,并且为重试策略提供了与语言无关的一致实现。例如,可以定义一个类似于以下示例的策略:apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: httpbin spec: hosts: - 'httpbin' http: - route: - destination: host: httpbin retries: attempts: 3 perTryTimeout: 5s通过这种简单的配置,通过Sidecar代理或入口网关对后端服务的请求最多可以重试3次,每次尝试都将有5秒钟的超时时间。Sidecar确定重试之间的间隔,并在两次尝试之间故意引入抖动,以避免轰炸过载的服务。熔断机制发生故障时,熔断器用于优化出站请求的行为。熔断器不会重复向无响应的服务发出请求,而是观察在给定时间段内发生的故障数。如果错误率超过阈值,则熔断器将断开请求,并且所有后续请求都将失败,直到熔断器被关闭为止。异常检测OutlierDetection是服务网格中的熔断实现,用于跟踪上游服务中每个主机的状态,支持HTTP和TCP类型的服务。对于HTTP服务来说,在预定义的时间段内,持续返回API调用的5xx错误的主机将在连接池中被移除。对于TCP服务来说,连接超时或连接失败将被认为异常。熔断器通过使用DestinationRule定义:apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: httpbin spec: host: httpbin trafficPolicy: outlierDetection: consecutiveErrors: 3 interval: 5s baseEjectionTime: 5m maxEjectionPercent: 100指定的outlierDetection流量策略将应用于每个单独的实例, 摘除在5秒钟内连续三次失败的服务实例, 并保持摘除至少5分钟。可以摘除所有实例, 即比例为100%。maxEjectionPercent设置与负载平衡有关。服务网格会维护负载均衡池,并从该池中摘除失败的实例。默认情况下,它会从负载平衡池中弹出最多10%的所有可用实例。阿里云服务网格(简称ASM)是一个统一管理微服务应用流量、兼容Istio的托管式平台。通过流量控制、网格观测以及服务间通信安全等功能,服务网格ASM可以全方位地简化您的服务治理,并为运行在异构计算基础设施上的服务提供统一的管理能力,适用于ACK Kubernetes集群、ASK Serverless Kubernetes集群、边缘集群、ECS虚拟机以及自建Kubernetes集群。具体参见:https://www.aliyun.com/product/servicemesh
简介 Envoy是一个高性能、可编程的L3 / L4和L7代理,被服务网格ASM作为数据面的代理使用。Envoy的连接和流量处理的核心是网络过滤器(Network Filter),该过滤器一旦融合进滤器链(Filter Chain),就可以实现用于访问控制、数据或协议转换、数据增强、审计等高级功能。通过添加新的过滤器Filter,可以用来扩展Envoy的已有功能集。当前有两种方法可以添加新的过滤器: 静态预编译:将其他过滤器Filter集成到Envoy的源代码中,并编译新的Envoy版本。这种方法的缺点是您需要维护自己的Envoy版本,并不断使其与官方发行版保持同步。此外,由于Envoy是用C++实现的,因此新开发的过滤器Filter也必须用C++实现。 动态运行时加载:在运行时将新的过滤器动态加载到Envoy代理中。 显而易见,从使用难易角度来看,第二种方式极大地简化了扩展Envoy的过程。这种解决方案依赖于一种称之为WebAssembly(WASM)的新技术,它是一种有效的可移植二进制指令格式,提供了可嵌入和隔离的执行环境。 ASM与WASM 阿里云服务网格ASM产品中提供了对WebAssembly(WASM)技术的支持,如下图所示,服务网格使用人员可以把扩展的WASM Filter通过ASM部署到数据面集群中相应的Envoy代理中。通过这种过滤器扩展机制,可以轻松扩展Envoy的功能并将其在服务网格中的应用推向了新的高度。 为什么要使用WASM Filter 使用WebAssembly(WASM)实现过滤器Filter的扩展,可以得到如下优势: 敏捷性:过滤器Filter可以动态加载到正在运行的Envoy进程中,而无需停止或重新编译。 可维护性:不必更改Envoy自身基础代码库即可扩展其功能。 多样性:可以将流行的编程语言(例如C / C ++和Rust)编译为WASM,因此开发人员可以使用他们选择的编程语言来实现过滤器Filter。 可靠性和隔离性:过滤器Filter会被部署到VM沙箱中,因此与Envoy进程本身是隔离的;即使当WASM Filter出现问题导致崩溃时,它也不会影响Envoy进程。 安全性:过滤器Filter通过预定义API与Envoy代理进行通信,因此它们可以访问并只能修改有限数量的连接或请求属性。 当前WebAssembly(WASM)实现过滤器Filter的扩展,也需要考虑以下缺点是否可以容忍: 性能约为C++编写的原生静态编译的Filter的70%。 由于需要启动一个或多个WASM虚拟机,因此会消耗一定的内存使用量。 使用Proxy-WASM SDK构建过滤器 Envoy Proxy在基于堆栈的虚拟机中运行WASM过滤器,因此过滤器的内存与主机环境是隔离的。Envoy代理与WASM过滤器之间的所有交互都是通过Envoy Proxy-WASM SDK提供的功能实现的。Envoy Proxy-WASM SDK提供了多种编程语言的实现,包括C++、Rust、AssemblyScript以及处于实验中的golang等。此外,社区也在推动相应的WebAssembly (Wasm) for Proxies (Proxy-Wasm)应用二进制接口ABI规范,具体可以参考:https://github.com/proxy-wasm/spec。 构建WASM Filter的最简单方法是使用Docker,参考https://github.com/proxy-wasm/proxy-wasm-cpp-sdk#docker所述,使用C++ Envoy Proxy-WASM SDK创建一个Docker镜像。 或者直接使用已构建好的镜像:registry.cn-hangzhou.aliyuncs.com/acs/wasmsdk:v0.1 参照https://github.com/proxy-wasm/proxy-wasm-cpp-sdk#creating-a-project-for-use-with-the-docker-build-image 所述,创建一个项目并使用上述Docker镜像进行构建。 具体开发过程在此不再赘述,具体可参见:https://github.com/proxy-wasm/proxy-wasm-cpp-sdk#webassembly-for-proxies-c-sdk 切换到项目根目录,执行如下命令构建WASM: docker run -v $PWD:/work -w /work registry.cn-hangzhou.aliyuncs.com/acs/wasmsdk:v0.1 /build_wasm.sh 在ASM中部署启用WASM Filter 创建一个configmap,用于保存WASM过滤器的二进制文件内容。例如,在命名空间default下,创建一个名称为wasm-example-filter的configmap,并将WASM过滤器的二进制文件example-filter.wasm保存到该configmap中。 kubectl create configmap -n default wasm-example-filter --from-file=example-filter.wasm 使用以下两个annotation将WASM过滤器的二进制文件注入到应用程序对应的Kubernetes服务中: sidecar.istio.io/userVolume: '[{"name":"wasmfilters-dir","configMap": {"name": "wasm-example-filter"}}]' sidecar.istio.io/userVolumeMount: '[{"mountPath":"/var/local/lib/wasm-filters","name":"wasmfilters-dir"}]' 执行以下命令更新productpage-v1: kubectl patch deployment productpage-v1 -p '{"spec":{"template":{"metadata":{"annotations":{"sidecar.istio.io/userVolume":"[{\"name\":\"wasmfilters-dir\",\"configMap\": {\"name\": \"wasm-example-filter\"}}]","sidecar.istio.io/userVolumeMount":"[{\"mountPath\":\"/var/local/lib/wasm-filters\",\"name\":\"wasmfilters-dir\"}]"}}}}}' 执行以下命令更新details-v1: kubectl patch deployment details-v1 -p '{"spec":{"template":{"metadata":{"annotations":{"sidecar.istio.io/userVolume":"[{\"name\":\"wasmfilters-dir\",\"configMap\": {\"name\": \"wasm-example-filter\"}}]","sidecar.istio.io/userVolumeMount":"[{\"mountPath\":\"/var/local/lib/wasm-filters\",\"name\":\"wasmfilters-dir\"}]"}}}}}' 现在,您可以在istio-proxy容器中的路径/var/local/lib/wasm-filters下,找到WASM过滤器的二进制文件: kubectl exec -it deployment/productpage-v1 -c istio-proxy -- ls /var/local/lib/wasm-filters/ kubectl exec -it deployment/details-v1 -c istio-proxy -- ls /var/local/lib/wasm-filters/ 要使WASM过滤器在处理针对应用服务productpage的流量时,能够以DEBUG日志级别记录,执行如下命令: kubectl port-forward deployment/productpage-v1 15000 curl -XPOST "localhost:15000/logging?wasm=debug" 同样地, 要使WASM过滤器在处理针对应用服务productpage的流量时,能够以DEBUG日志级别记录,执行如下命令: kubectl port-forward deployment/details-v1 15000 curl -XPOST "localhost:15000/logging?wasm=debug" 将WASM过滤器插入到应用服务productpage的HTTP级别过滤器链中,执行如下命令: apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: productpage-v1-examplefilter spec: configPatches: - applyTo: HTTP_FILTER match: context: SIDECAR_INBOUND listener: filterChain: filter: name: envoy.http_connection_manager subFilter: name: envoy.router patch: operation: INSERT_BEFORE value: config: config: name: example-filter rootId: my_root_id vmConfig: code: local: filename: /var/local/lib/wasm-filters/example-filter.wasm runtime: envoy.wasm.runtime.v8 vmId: example-filter allow_precompiled: true name: envoy.filters.http.wasm workloadSelector: labels: app: productpage version: v1 通过在浏览器中访问入口网关的地址, 将一些流量发送到productpage服务上, 在页面响应中,可以看到过滤器的头添加到响应头中,如下图所示。 将WASM过滤器插入到应用服务details的HTTP级别过滤器链中,执行如下命令: apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: details-v1-examplefilter spec: configPatches: - applyTo: HTTP_FILTER match: context: SIDECAR_INBOUND listener: filterChain: filter: name: envoy.http_connection_manager subFilter: name: envoy.router patch: operation: INSERT_BEFORE value: config: config: name: example-filter rootId: my_root_id vmConfig: code: local: filename: /var/local/lib/wasm-filters/example-filter.wasm runtime: envoy.wasm.runtime.v8 vmId: example-filter allow_precompiled: true name: envoy.filters.http.wasm workloadSelector: labels: app: details version: v1 将一些流量发送到details服务上 : kubectl exec -ti deploy/productpage-v1 -c istio-proxy -- curl -v http://details:9080/details/123 在响应中,可以看到过滤器的头添加到响应头中: kubectl exec -ti deploy/productpage-v1 -c istio-proxy -- curl -v http://details:9080/details/123 * Trying 172.31.13.58... * TCP_NODELAY set * Connected to details (172.31.13.58) port 9080 (#0) > GET /details/123 HTTP/1.1 > Host: details:9080 > User-Agent: curl/7.58.0 > Accept: */* > < HTTP/1.1 200 OK xxxxxxx < resp-header-demo: added by our filter xxxxx * Connection #0 to host details left intact xxxxx
单控制平面拓扑下,多个 Kubernetes 集群共同使用在其中一个集群上运行的单个 Istio 控制平面。控制平面的 Pilot 管理本地和远程集群上的服务,并为所有集群配置 Envoy Sidecar 代理。 集群感知的服务路由 Istio 1.1 中引入了集群感知的服务路由能力,在单一控制平面拓扑配置下,使用 Istio 的 Split-horizon EDS(水平分割端点发现服务)功能可以通过其入口网关将服务请求路由到其他集群。基于请求源的位置,Istio 能够将请求路由到不同的端点。 在该配置中,从一个集群中的 Sidecar 代理到同一集群中的服务的请求仍然被转发到本地服务 IP。如果目标工作负载在其他集群中运行,则使用远程集群的网关 IP 来连接到该服务。 如图所示,主集群 cluster1 运行全套的 Istio 控制平面组件,同时集群 cluster2 仅运行 Istio Citadel、Sidecar Injector 和 Ingress 网关。不需要 VPN 连接,不同集群中的工作负载之间也不需要直接网络访问。 从共享的根 CA 为每个集群的 Citadel 生成中间 CA 证书,共享的根 CA 启用跨不同集群的双向 TLS 通信。为了便于说明,我们将 samples/certs 目录下 Istio 安装中提供的示例根 CA 证书用于两个集群。在实际部署中,你可能会为每个集群使用不同的 CA 证书,所有 CA 证书都由公共根 CA 签名。 在每个 Kubernetes 集群中(包括示例中的集群 cluster1 与 cluster2)使用以下命令为生成的 CA 证书创建 Kubernetes 密钥: kubectl create namespace istio-system kubectl create secret generic cacerts -n istio-system \ --from-file=samples/certs/ca-cert.pem \ --from-file=samples/certs/ca-key.pem \ --from-file=samples/certs/root-cert.pem \ --from-file=samples/certs/cert-chain.pem Istio 控制平面组件 在部署全套 Istio 控制平面组件的集群 cluster1 中,按照以下步骤执行: 1.安装 Istio 的 CRD 并等待几秒钟,以便将它们提交给 Kubernetes API 服务器,如下所示: for i in install/kubernetes/helm/istio-init/files/crd*yaml; do kubectl apply -f $i; done 2.然后开始在集群 cluster1 中部署 Istio 控制平面。 如果 helm 依赖项缺失或者不是最新的,可以通过 helm dep update 来更新这些依赖项。需要注意的是,因为没有使用 istio-cni,可以暂时将其从依赖项 requirements.yaml 中去掉再执行更新操作。具体命令如下所示: helm template --name=istio --namespace=istio-system \ --set global.mtls.enabled=true \ --set security.selfSigned=false \ --set global.controlPlaneSecurityEnabled=true \ --set global.meshExpansion.enabled=true \ --set global.meshNetworks.network2.endpoints[0].fromRegistry=n2-k8s-config \ --set global.meshNetworks.network2.gateways[0].address=0.0.0.0 \ --set global.meshNetworks.network2.gateways[0].port=15443 \ install/kubernetes/helm/istio > ./istio-auth.yaml 请注意,网关地址设置为 0.0.0.0。这是一个临时占位符值,在集群 cluster2 部署之后将更新为其网关的公共 IP 值。 将 Istio 部署到 cluster1,如下所示: kubectl apply -f ./istio-auth.yaml 确保上述步骤在 Kubernetes 集群中执行成功。 创建网关以访问远程服务,如下所示: kubectl create -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: cluster-aware-gateway namespace: istio-system spec: selector: istio: ingressgateway servers: - port: number: 15443 name: tls protocol: TLS tls: mode: AUTO_PASSTHROUGH hosts: - "*" EOF 上述网关配置了一个专用端口 15443 用来将传入流量传递到请求的 SNI 标头中指定的目标服务,从源服务到目标服务一直使用双向 TLS 连接。 请注意虽然该网关定义应用于集群 cluster1,但因为两个集群都与同一个 Pilot 进行通信,此网关实例同样也适用于集群 cluster2。 istio-remote 组件 在另一集群 cluster2 中部署 istio-remote 组件,按照以下步骤执行: 1.首先获取集群 cluster1 的入口网关地址,如下所示: export LOCAL_GW_ADDR=$(kubectl get svc --selector=app=istio-ingressgateway \ -n istio-system -o jsonpath="{.items[0].status.loadBalancer.ingress[0].ip}") 通过执行以下命令,使用 Helm 创建 Istio remote 部署 YAML 文件: helm template --name istio-remote --namespace=istio-system \ --values install/kubernetes/helm/istio/values-istio-remote.yaml \ --set global.mtls.enabled=true \ --set gateways.enabled=true \ --set security.selfSigned=false \ --set global.controlPlaneSecurityEnabled=true \ --set global.createRemoteSvcEndpoints=true \ --set global.remotePilotCreateSvcEndpoint=true \ --set global.remotePilotAddress=${LOCAL_GW_ADDR} \ --set global.remotePolicyAddress=${LOCAL_GW_ADDR} \ --set global.remoteTelemetryAddress=${LOCAL_GW_ADDR} \ --set gateways.istio-ingressgateway.env.ISTIO_META_NETWORK="network2" \ --set global.network="network2" \ install/kubernetes/helm/istio > istio-remote-auth.yaml 2.将 Istio remote 组件部署到 cluster2,如下所示: kubectl apply -f ./istio-remote-auth.yaml 确保上述步骤在 Kubernetes 集群中执行成功。 3.更新集群 cluster1 的配置项 istio,获取集群 cluster2 的入口网关地址,如下所示: export REMOTE_GW_ADDR=$(kubectl get --context=$CTX_REMOTE svc --selector=app= istio-ingressgateway -n istio-system -o jsonpath="{.items[0].status.loadBalancer.ingress [0].ip}") 在集群 cluster1 中编辑命名空间 istio-system 下的配置项 istio,替换 network2 的网关地址,从 0.0.0.0 变成集群 cluster2 的入口网关地址 ${REMOTE_GW_ADDR}。保存后,Pilot 将自动读取更新的网络配置。 4.创建集群 cluster2 的 Kubeconfig。通过以下命令,在集群 cluster2 上创建服务账号 istio-multi 的 Kubeconfig,并保存为文件 n2-k8s-config: CLUSTER_NAME="cluster2" SERVER=$(kubectl config view --minify=true -o "jsonpath={.clusters[].cluster.server}") SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}') CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}") TOKEN=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode) cat <<EOF > n2-k8s-config apiVersion: v1 kind: Config clusters: - cluster: certificate-authority-data: ${CA_DATA} server: ${SERVER} name: ${CLUSTER_NAME} contexts: - context: cluster: ${CLUSTER_NAME} user: ${CLUSTER_NAME} name: ${CLUSTER_NAME} current-context: ${CLUSTER_NAME} users: - name: ${CLUSTER_NAME} user: token: ${TOKEN} EOF 5.将集群 cluster2 加入到 Istio 控制平面。 在集群 clusterl 执行以下命令,将上述生成的集群 cluster2 的 kubeconfig 添加到集群 cluster1 的 secret 中,执行这些命令后,集群 cluster1 中的 Istio Pilot 将开始监听集群 cluster2 的服务和实例,就像监听集群 cluster1 中的服务与实例一样: kubectl create secret generic n2-k8s-secret --from-file n2-k8s-config -n istio-system kubectl label secret n2-k8s-secret istio/multiCluster=true -n istio-system 部署示例应用 为了演示跨集群访问,在第一个 Kubernetes 集群 cluster1 中部署 sleep 应用服务和版本 v1 的 helloworld 服务,在第二个集群 cluster2 中部署版本 v2 的 helloworld 服务,然后验证 sleep 应用是否可以调用本地或者远程集群的 helloworld 服务。 1.部署 sleep 和版本 v1 的 helloworld 服务到第一个集群 cluster1 中,执行如下命令: kubectl create namespace app1 kubectl label namespace app1 istio-injection=enabled kubectl apply -n app1 -f samples/sleep/sleep.yaml kubectl apply -n app1 -f samples/helloworld/service.yaml kubectl apply -n app1 -f samples/helloworld/helloworld.yaml -l version=v1 export SLEEP_POD=$(kubectl get -n app1 pod -l app=sleep -o jsonpath={.items..metadata.name}) 2.部署版本 v2 的 helloworld 服务到第二个集群 cluster2 中,执行如下命令: kubectl create namespace app1 kubectl label namespace app1 istio-injection=enabled kubectl apply -n app1 -f samples/helloworld/service.yaml kubectl apply -n app1 -f samples/helloworld/helloworld.yaml -l version=v2 3.登录到命名空间 istio-system 下的 istio-pilot 容器中,运行 curl localhost:8080/v1/registration | grep helloworld -A 11 -B 2 命令,如果得到如下类似的结果就说明版本 v1 与 v2 的 helloworld 服务都已经注册到 Istio 控制平面中了: 4.验证在集群 cluster1 中的 sleep 服务是否可以正常调用本地或者远程集群的 helloworld 服务,在集群 cluster1 下执行如下命令: kubectl exec -it -n app1 $SLEEP_POD sh 登录到容器中,运行 curl helloworld.app1:5000/hello。 如果设置正确,则在返回的调用结果中可以看到两个版本的 helloworld 服务,同时可以通过查看 sleep 容器组中的 istio-proxy 容器日志来验证访问的端点 IP 地址,返回结果如下所示:
服务网格作为一个改善服务到服务通信的专用基础设施层,是云原生范畴中最热门的话题。随着容器愈加流行,服务拓扑也频繁变动,这就需要更好的网络性能。服务网格能够通过服务发现、路由、负载均衡、心跳检测和支持可观测性,帮助我们管理网络流量。服务网格试图为无规则的复杂的容器问题提供规范化的解决方案。 服务网格也可以用于混沌工程 —— “一门在分布式系统上进行实验的学科,目的是构建能够应对极端条件的可靠系统”。服务网格能够将延迟和错误注入到环境中,而不需要在每个主机上安装一个守护进程。 容器是云原生应用的基石,通过应用容器化,使得应用开发部署更加敏捷、迁移更加灵活,并且这些实现都是基于标准化的。而容器编排则是更近一步,能够更加有效地编排资源、更加高效地调度利用这些资源。而到了云原生时代,在 Kubernetes 基础架构之上,结合 Istio 服务网格,提供了多云、混合云的支持能力,针对微服务提供了有效的治理能力,并以 Kubernetes 和 Istio 为基础,提供了针对特定应用负载的不同支持,例如针对 Kubeflow 服务的流量治理、为 Knative 提供负载的路由管理能力等。 尽管 Service Mesh 在云原生系统方面的应用已经有了快速的增长,但仍然存在巨大的提升空间。无服务器(Serverless)计算正好需要 Service Mesh 的命名和链接模型,这让 Service Mesh 在云原生生态系统中的角色得到了彰显。服务识别和访问策略在云原生环境中仍显初级,而 Service Mesh 毫无疑问将成为这方面不可或缺的基础。就像 TCP/IP 一样,Service Mesh 将在底层基础设施这条道路上更进一步。 混合云可以采用多种形式。通常,混合云指的是跨公有云和私有(内部部署)云运行,而多云意味着跨多个公有云平台运行。 采用混合云或多云架构可以为你的组织带来诸多好处。例如,使用多个云提供商可以帮助你避免供应商锁定,能够让你为实现目标选择最佳的云服务。使用云和本地环境,你可以同时享受云的优势(灵活性、可扩展性、成本降低)和本地的好处(安全性、低延迟、硬件复用)。如果你是首次迁移到云端,采用混合云步骤可以让你按照自己的节奏,以最适合你业务的方式进行。 根据我们在公有云上的实践经验及从客户那里得到的信息,我们认为采用混合服务网络是简化云和本地环境中应用程序管理、安全性和可靠性的关键,无论你的应用程序是在容器中运行,或是在虚拟机中运行。 Istio 的一个关键特性是它为你的工作负载(例如 pod、job、基于 VM 的应用程序)提供服务抽象。当你转向混合拓扑时,这种服务抽象变得更加重要,因为现在你不只需要关注一个环境,而是需要关注若干个环境。 当你在一个 Kubernetes 集群上使用 Istio 时,可以获得包括可见性、细粒度流量策略、统一遥测和安全性在内的微服务的所有管理优势。但是当你在多个环境中使用 Istio 时,实际上是为应用程序提供了一个新的超级能力。因为 Istio 不仅仅是 Kubernetes 的服务抽象,也是一种在整个环境中标准化网络的方法。它是一种集中 API 管理并将 JWT 验证与代码分离的方法。它是跨云提供商的安全、零信任网络的快速通道。 那么所有这些魔法是如何发生的呢?混合 Istio 是指一组 Istio Sidecar 代理,每一个 Envoy 代理位于所有服务的旁边,而这些服务可能运行在不同环境中的每一个虚拟机、每一个容器中,而且这些 Sidecar 代理之前互相知道如何跨边界交互。这些 Envoy Sidecar 代理可能由一个中央 Istio 控制平面管理,或由每个环境中运行的多个控制平面管理。 多集群部署管理 服务网格本质上是将一组单独的微服务组合成单个可控的复合应用程序,Istio 作为一种服务网格,也是旨在单一管理域下监视和管理协作微服务网络。对于特定大小的应用程序,所有微服务是可以在单个编排平台如一个 Kubernetes 集群上运行的。然而,由于规模不断增大或者冗余等原因,大多数应用程序最终将需要分发一些服务在其他地方运行。 社区越来越关注在多个集群上运行工作负载,以实现更好的扩展,故障可以更好地隔离,从而提升应用程序的敏捷性。Istio v1.0 开始支持一些多集群功能,并在之后的版本中添加了新功能。 Istio 服务网格支持许多可能的拓扑结构,用于在单个集群之外分发应用程序的服务,有两种常见的模式或用例:单网格和网格联合。顾名思义,单个网格将多个集群组合成一个单元,由一个 Istio 控制平面管理;它可以实现为一个物理控制平面,也可以实现为一组控制平面,同时所有控制平面都能通过复制配置保持同步。而网格联合则会将多个集群分离作为单独的管理域,有选择地完成集群之间的连接,仅将服务的子集暴露给其他集群;自然它的实现会包含多个控制平面。 具体来说,这些不同的拓扑结构包括以下几个方面: 网格中的服务可以使用服务条目(Service Entry)来访问独立的外部服务或访问由另一个松散耦合的服务网格公开的服务,通常称为网格联邦(Mesh Federation)。这种拓扑适合于互相独立并且网络隔离、只能通过公网交互的多集群的场景; 支持在虚拟机或物理裸机上运行的服务进行服务网格扩展,通常称为网格联合(Mesh Expansion)。在前面章节中,我们已经讲述了这种 Kubernetes 集群与虚拟机、物理裸机之间混合部署的场景; 把来自多个集群的服务组合到单个服务网格中,通常称为多集群网格(Multicluster Mesh)。根据网络拓扑结构的不同,多集群网格通常分为单控制平面 VPN 连接、单控制平面网关连接以及多控制平面拓扑。 单控制平面 VPN 连接拓扑 作为基准,在 Istio 的 1.1 版本之前,Istio 1.0 多集群仅支持使用单网格设计。它允许多个集群连接到网格中,但所有集群都在一个共享网络上。也就是说,所有集群中所有 pod 和服务的 IP 地址都是可直接路由的,不会发生冲突,同时保证在一个集群中分配的IP地址不会在另一个集群中同时重用。 在这种拓扑配置下,在其中一个集群上运行单个 Istio 控制平面。该控制平面的 Pilot 管理本地和远程集群上的服务,并为所有集群配置 Envoy 代理。这种方法在所有参与集群都具有 VPN 连接的环境中效果最佳,因此可以使用相同的 IP 地址从其他任何地方访问网格中的每个 pod。 在此配置中,Istio 控制平面部署在其中一个集群上,而所有其他集群运行更简单的远程 Istio 配置,该配置将它们连接到单个 Istio 控制平面,该平面将所有 Envoy 管理为单个网格。各个集群上的 IP 地址不允许重叠,并且远程集群上的服务的 DNS 解析不是自动的。用户需要在每个参与集群上复制服务,这样每个集群中的 Kubernetes 集群服务和应用程序都能够将其内部 Kubernetes 网络暴露给其他集群。一旦一个或多个远程 Kubernetes 集群连接到 Istio 控制平面,Envoy 就可以与单个控制平面通信并形成跨多个集群的网状网络。 前提约束 事实上,我们已经了解到网格、集群和网络之间的存在各种约束,例如,在某些环境中,网络和集群直接相关。Istio单网格设计下的单控制平面VPN连接拓扑需要满足以下几个条件: 运行 Kubernetes 1.9 或更高版本的两个或更多集群; 能够在其中一个集群上部署 Istio 控制平面; RFC1918 网络、VPN 或满足以下要求的更高级网络技术: 单个集群 pod CIDR 范围和服务 CIDR 范围在多集群环境中必须是唯一的,并且应当不重叠; 每个集群中的所有 pod CIDR 必须可以相互路由 所有 Kubernetes 控制平面 API 服务器必须可以相互路由。 此外,为了跨集群支持 DNS 名称解析,必须确保在所有需要跨集群服务调用的集群中定义对应的命名空间、服务和服务账户;例如,集群 cluster1 中命名空间 ns1 的服务 service1 需要调用集群 cluster2 中命名空间 ns2 的服务 service2,那么在集群 cluster1 中为了支持服务名的 DNS 解析,需要在集群 cluster1 中创建一个命名空间 ns2 以及该命名空间下的服务 service2。 以下示例中的两个 Kubernetes 集群的网络假定已经满足上述要求,每个集群中的 pod 都能够互相路由,也就是说网络可通并且端口是可访问的(如果采用的是类似于阿里云的公有云服务,请确保这些端口在安全组规则下是可以访问的;否则服务间的调用会受到影响)。 两个 Kubernetes 集群的 pod CIDR 范围和服务 CIDR 范围定义如下表所示: 拓扑架构 从图中可以看到整个多集群拓扑中只会在一个 Kubernetes 集群上安装 Istio 控制平面。这个安装 Istio 控制平面的集群通常被称为本地集群,所有其它集群称为远程集群。 这些远程集群只需要安装 Istio 的 Citadel 和 Sidecar Injector 准入控制器,具有较小的 Istio 占用空间,Citadel 用于这些远程集群的安全管理,Sidecar Injector 准入控制器用于控制平面中的自动注入和数据平面中工作负载的 Sidecar 代理功能。 在这个架构中,Pilot 可以访问所有集群中的所有 Kubernetes API 服务器,因此它具有全局网络访问视图。Citadel 和 Sidecar Injector 准入控制器则只会在集群本地范围内运行。每个集群都有唯一的 pod 和服务 CIDR,除此之外,集群之间还有一个共享的扁平网络,以保证能直接路由到任何工作负载,包括到 Istio 的控制平面。例如,远程集群上的 Envoy 代理需要从 Pilot 获得配置,检查并报告给 Mixer 等。 启用双向 TLS 通信 如果在多个集群中启用跨集群的双向 TLS 通信,就需要按照如下方式在各个集群中进行部署配置。首先,从共享的根 CA 为每个集群的 Citadel 生成中间 CA 证书,共享的根 CA 启用跨不同集群的双向 TLS 通信。为了便于说明,我们将 samples/certs 目录下 Istio 安装中提供的示例根 CA 证书用于两个集群。在实际部署中,你可能会为每个集群使用不同的 CA 证书,所有 CA 证书都由公共根 CA 签名。 在每个 Kubernetes 集群中(包括示例中的集群 cluster1 与 cluster2)创建密钥。使用以下的命令为生成的 CA 证书创建 Kubernetes 密钥: kubectl create namespace istio-system kubectl create secret generic cacerts -n istio-system \ --from-file=samples/certs/ca-cert.pem \ --from-file=samples/certs/ca-key.pem \ --from-file=samples/certs/root-cert.pem \ --from-file=samples/certs/cert-chain.pem 当然,如果你的环境只是开发测试或者不需要启用双向 TLS 通信,上述步骤完全可以跳过。 部署本地控制平面 在所谓的本地集群上安装一个 Istio 控制平面的过程,与在单集群上安装 Istio 并没有太多差别,需要注意的一点是如何配置 Envoy 代理用于管理直接访问某个 IP 范围内的外部服务的参数。如果是使用 Helm 安装 Istio,那么在 Helm 中有一个名为 global.proxy.includeIPRanges 的变量,确保该变量为“*”或者包括本地集群、所有远程集群的 pod CIDR 范围和服务 CIDR。 可以通过查看命名空间 istio-system 下的配置项 istio-sidecar-injector 中的 traffic.sidecar.istio.io/includeOutboundIPRanges 来确认 global.proxy.includeIPRanges 参数设置,如下所示: kubectl get configmap istio-sidecar-injector -n istio-system -o yaml| grep includeOutboundIPRanges 在部署 Istio 控制平面组件的集群 cluster1 中,按照以下步骤执行。 如果启用了双向 TLS 通信,则需要如下配置参数: helm template --namespace=istio-system \ --values install/kubernetes/helm/istio/values.yaml \ --set global.mtls.enabled=true \ --set security.selfSigned=false \ --set global.controlPlaneSecurityEnabled=true \ install/kubernetes/helm/istio > istio-auth.yaml kubectl apply -f istio-auth.yaml 如果不需要启用双向 TLS 通信,配置参数则需要做出如下修改: helm template --namespace=istio-system \ --values install/kubernetes/helm/istio/values.yaml \ --set global.mtls.enabled=false \ --set security.selfSigned=true \ --set global.controlPlaneSecurityEnabled=false \ install/kubernetes/helm/istio > istio-noauth.yaml kubectl apply -f istio-noauth.yaml 修改 Istio 服务 istio-pilot、istio-telemetry、istio-policy 及 zipkin 的类型为内网负载均衡,将这些服务以内网方式暴露给远程集群使用。不同的云厂商实现机制不尽相同,但大都是通过修改 annotation 的方式实现。针对阿里云容器服务来说,设置为内网负载均衡的方式非常简单,只需要添加如下 annotation 到服务的 YAML 定义中即可:service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet。 此外,需要按照图中的端口定义为每一个服务进行设置。 istio-pilot 服务端口如表 1 所示。 istio-telemetry 服务端口如表 2 所示 istio-policy 服务端口如表 3 所示。 zipkin 服务端口如表 4 所示。 安装 istio-remote 在本地集群中安装完控制平面之后,必须将 istio-remote 组件部署到每个远程 Kubernetes 集群。等待 Istio 控制平面完成初始化,然后再执行本节中的步骤。你必须在 Istio 控制平面集群上运行这些操作以捕获 Istio 控制平面服务端点,例如上述提到的 Istio 服务 istio-pilot、istio-telemetry、istio-policy 以及 zipkin。 在远程集群 cluster2 中部署 Istio-remote 组件,按照以下步骤执行: 1.在本地集群上使用以下命令设置环境变量: export PILOT_IP=$(kubectl -n istio-system get service istio-pilot -o jsonpath='{.status.loadBalancer.ingress[0].ip}') export POLICY_IP=$(kubectl -n istio-system get service istio-policy -o jsonpath='{.status.loadBalancer.ingress[0].ip}') export TELEMETRY_IP=$(kubectl -n istio-system get service istio-telemetry -o jsonpath='{.status.loadBalancer.ingress[0].ip}') export ZIPKIN_IP=$(kubectl -n istio-system get service zipkin -o jsonpath='{.status.loadBalancer.ingress[0].ip}') echo $PILOT_IP $POLICY_IP $TELEMETRY_IP $ZIPKIN_IP 2.如果在多个集群中启用跨集群的双向 TLS 通信,就需要在集群中进行部署配置。 当然,如果你的环境只是开发测试或者不需要启用双向 TLS 通信的话,该步骤完全可以跳过。在远程 Kubernetes 集群 cluster2 上运行以下命令,在集群中为生成的 CA 证书创建 Kubernetes 密钥: kubectl create namespace istio-system kubectl create secret generic cacerts -n istio-system \ --from-file=samples/certs/ca-cert.pem \ --from-file=samples/certs/ca-key.pem \ --from-file=samples/certs/root-cert.pem \ --from-file=samples/certs/cert-chain.pem 3.在远程 Kubernetes 集群 cluster2 上,通过执行以下命令,使用 Helm 创建 Istio remote 部署 YAML 文件。 如果启用了双向 TLS 通信,则需要如下配置参数: helm template install/kubernetes/helm/istio \ --name istio-remote \ --namespace istio-system \ --values install/kubernetes/helm/istio/values-istio-remote.yaml \ --set global.mtls.enabled=true \ --set security.selfSigned=false \ --set global.controlPlaneSecurityEnabled=true \ --set global.remotePilotCreateSvcEndpoint=true \ --set global.remotePilotAddress=${PILOT_IP} \ --set global.remotePolicyAddress=${POLICY_IP} \ --set global.remoteTelemetryAddress=${TELEMETRY_IP} --set global.remoteZipkinAddress=${ZIPKIN_IP} > istio-remote-auth.yaml 然后将 Istio remote 组件部署到 cluster2,如下所示: kubectl apply -f ./istio-remote-auth.yaml 如果不需要启用双向 TLS 通信,配置参数则需要做出如下修改: helm template install/kubernetes/helm/istio \ --name istio-remote \ --namespace istio-system \ --values install/kubernetes/helm/istio/values-istio-remote.yaml \ --set global.mtls.enabled=false \ --set security.selfSigned=true \ --set global.controlPlaneSecurityEnabled=false \ --set global.remotePilotCreateSvcEndpoint=true \ --set global.remotePilotAddress=${PILOT_IP} \ --set global.remotePolicyAddress=${POLICY_IP} \ --set global.remoteTelemetryAddress=${TELEMETRY_IP} --set global.remoteZipkinAddress=${ZIPKIN_IP} > istio-remote-noauth.yaml 然后将 Istio remote 组件部署到 cluster2,如下所示: kubectl apply -f ./istio-remote-noauth.yaml 确保上述步骤在 Kubernetes 集群中执行成功。 4.创建集群 cluster2 的 Kubeconfig。 安装 Istio-remote Helm chart 后,在远程集群中创建了一个叫 istio-multi 的 Kubernetes 服务帐号,该服务帐号用于最小化 RBAC 访问请求,对应的集群角色定义如下: kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: istio-reader rules: - apiGroups: [''] resources: ['nodes', 'pods', 'services', 'endpoints'] verbs: ['get', 'watch', 'list'] 下面的过程通过使用先前所述的 istio-multi 服务帐号凭证生成一个远程集群的 kubeconfig 配置文件。通过以下命令,在集群 cluster2 上创建服务帐号 istio-multi 的 Kubeconfig,并保存为文件 n2-k8s-config: CLUSTER_NAME="cluster2" SERVER=$(kubectl config view --minify=true -o "jsonpath={.clusters[].cluster.server}") SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}') CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}") TOKEN=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode) cat <<EOF > n2-k8s-config apiVersion: v1 kind: Config clusters: - cluster: certificate-authority-data: ${CA_DATA} server: ${SERVER} name: ${CLUSTER_NAME} contexts: - context: cluster: ${CLUSTER_NAME} user: ${CLUSTER_NAME} name: ${CLUSTER_NAME} current-context: ${CLUSTER_NAME} users: - name: ${CLUSTER_NAME} user: token: ${TOKEN} EOF 5.将集群 cluster2 加入 Istio Pilot 所在集群中。 在集群 dusterl 执行以下命令,将上述生成的集群 cluster2 的 kubeconfig 添加到集群 cluster1 的 secret 中。执行这些命令后,集群 cluster1 中的 Istio Pilot 将开始监听集群 cluster2 的服务和实例,就像监听集群 cluster1 中的服务与实例一样: kubectl create secret generic n2-k8s-secret --from-file n2-k8s-config -n istio-system kubectl label secret n2-k8s-secret istio/multiCluster=true -n istio-system 部署示例应用 为了演示跨集群访问,在第一个 Kubernetes 集群 cluster1 中部署 sleep 应用服务和版本 v1 的 helloworld 服务,在第二个集群 cluster2 中部署版本 v2 的 helloworld 服务,然后验证 sleep 应用是否可以调用本地或者远程集群的 helloworld 服务。 1.部署 sleep 和版本 v1 的 helloworld 服务到第一个集群 cluster1 中,执行如下命令: kubectl create namespace app1 kubectl label namespace app1 istio-injection=enabled kubectl apply -n app1 -f multicluster/sleep/sleep.yaml kubectl apply -n app1 -f multicluster/helloworld/service.yaml kubectl apply -n app1 -f multicluster/helloworld/helloworld.yaml -l version=v1 export SLEEP_POD=$(kubectl get -n app1 pod -l app=sleep -o jsonpath={.items..metadata.name}) 部署版本 v2 的 helloworld 服务到第二个集群 cluster2 中,执行如下命令: kubectl create namespace app1 kubectl label namespace app1 istio-injection=enabled kubectl apply -n app1 -f multicluster/helloworld/service.yaml kubectl apply -n app1 -f multicluster/helloworld/helloworld.yaml -l version=v2 验证在集群 cluster1 中的 sleep 服务是否可以正常调用本地或者远程集群的 helloworld 服务,在集群 cluster1 下执行如下命令: kubectl exec $SLEEP_POD -n app1 -c sleep -- curl helloworld.app1:5000/hello 如果设置正确,则在返回的调用结果中可以看到两个版本的 helloworld 服务,同时可以通过查看 sleep 容器组中的 istio-proxy 容器日志来验证访问的端点 IP 地址,返回结果如下所示: 验证 Istio 路由规则是否生效。创建针对上述两个版本的 helloworld 服务的路由规则,以便验证 Istio 配置是否可以正常工作。 创建 Istio 虚拟服务 VirtualService,执行如下命令: kubectl apply -n app1 -f multicluster/helloworld/virtualservice.yaml 接着,创建 Istio 目标规则 DestinationRule: 如果启用了双向 TLS 通信,则需要如下配置参数: kubectl apply -n app1 -f multicluster/helloworld/destinationrule.yaml 如果不需要启用双向 TLS 通信,配置参数则需要做出修改,在 YAML 定义中添加trafficPolicy.tls.mode:ISTIO_MUTUAL,定义如下所示: apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: helloworld spec: host: helloworld **trafficPolicy: tls: mode: ISTIO_MUTUAL** subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 通过执行命令 kubectl apply 创建启用双向 TLS 的 Istio 目标规则,如下所示: kubectl apply -n app1 -f multicluster/helloworld/destinationrule-auth.yaml 多次调用 helloworld 服务,只会返回版本 v2 的响应结果,如下所示:
Istio 是一个开源的服务网格,可为分布式微服务架构提供所需的基础运行和管理要素。随着各组织越来越多地采用云平台,开发者必须使用微服务设计架构以实现可移植性,而运维人员必须管理包含混合云部署和多云部署的大型分布式应用。Istio 采用一种一致的方式来保护、连接和监控微服务,降低了管理微服务部署的复杂性。 从架构设计上来看,Istio 服务网格在逻辑上分为控制平面和数据平面两部分。其中,控制平面 Pilot 负责管理和配置代理来路由流量,并配置 Mixer 以实施策略和收集遥测数据;数据平面由一组以 Sidecar 方式部署的智能代理(Envoy)组成,这些代理可以调节和控制微服务及 Mixer 之间所有的网络通信。作为代理,Envoy 非常适合服务网格的场景,但要发挥 Envoy 的最大价值,就需要使它很好地与底层基础设施或组件紧密配合。Envoy 构成了服务网格的数据平面,Istio 提供的支撑组件则是创建了控制平面。一方面,我们在 Envoy 中看到,可以使用静态配置文件或使用一组发现服务来配置一组服务代理,以便在运行时发现监听器、端点和集群。Istio 在 Pilot 中实现了这些 Envoy 代理的 xDS API。 另一方面,Envoy 的服务发现依赖于某种服务注册表来发现服务端点。Istio Pilot 实现了这个 API,但也将 Envoy 从任何特定的服务注册实现中抽象出来。当 Istio 部署在 Kubernetes 上时,Kubernetes 的服务注册表是 Istio 用于服务发现的。其它注册表也可以像 HashiCorp 的 Consul 那样使用。Envoy 数据平面完全不受这些实施细节的影响。 此外,Envoy 代理可以发出很多指标和遥测数据,这些遥测数据发送到何处,取决于 Envoy 的配置。Istio 提供遥测接收器 Mixer 作为其控制平面的一部分,Envoy 代理可以将这些数据发送到 Mixer。Envoy 还将分布式跟踪数据发送到开放式跟踪引擎(遵循 Open Tracing API)。Istio 可以支持兼容的开放式跟踪引擎并配置 Envoy 将其跟踪数据发送到该位置。 剖析 Istio 控制平面 Istio 的控制平面和 Envoy 的数据平面共同构成了一个引人注目的服务网格实现。两者都拥有蓬勃发展和充满活力的社区,并且面向下一代服务架构。Istio 是独立于平台的,可运行于各种环境中,包括跨云、内部部署、Kubernetes、Mesos 等。你可以在 Kubernetes 上部署 Istio 或在具有 Consul 的 Nomad 上部署。Istio 目前支持在 Kubernetes 上部署的服务、使用 Consul 注册的服务以及在虚拟机上部署的服务。 其中,控制平面部分包括了 Pilot、Mixer、Citadel 和 Galley 四个组件。参见 Istio 架构一图。 1. Pilot Istio 的 Pilot 组件用于管理流量,可以控制服务之间的流量流动和 API 调用,通过 Pilot 可以更好地了解流量,以便在问题出现之前发现问题。这使得调用更加可靠、网络更加强健,即使遇到不利条件也能让应用稳如磐石。借助 Istio 的 Pilot,你能够配置熔断器、超时和重试等服务级属性,并设置常见的连续部署任务,如金丝雀发布、A/B 测试和基于百分比拆分流量的分阶段发布。Pilot 为 Envoy 代理提供服务发现功能,为智能路由和弹性能力(如超时、重试、熔断器等)提供流量管理功能。Pilot 将控制流量行为的高级路由规则转换为特定于 Envoy 代理的配置,并在运行时将它们传播到 Envoy。此外,Istio 提供了强大的开箱即用故障恢复功能,包括超时、支持超时预算和变量抖动的重试机制、发往上游服务的并发连接和请求数限制、对负载均衡池中的每个成员进行的定期主动运行状况检查,以及被动运行状况检查。 Pilot 将平台特定的服务发现机制抽象化并将其合成为标准格式,符合数据平面 API 的任何 Sidecar 都可以使用这种标准格式。这种松散耦合使得 Istio 能够在多种环境下运行(例如 Kubernetes、Consul、Nomad),同时可保持用于流量管理的操作界面相同。 2. Mixer Istio 的 Mixer 组件提供策略控制和遥测收集功能,将 Istio 的其余部分与各个后端基础设施后端的实现细节隔离开来。Mixer 是一个独立于平台的组件,负责在服务网格上执行访问控制和使用策略,并从 Envoy 代理和其他服务收集遥测数据。代理提取请求级属性,发送到 Mixer 进行评估。 Mixer 中包括一个灵活的插件模型,使其能够接入到各种主机环境和后端基础设施,从这些细节中抽象出 Envoy 代理和 Istio 管理的服务。利用 Mixer,你可以精细控制网格和后端基础设施后端之间的所有交互。 与必须节省内存的 Sidecar 代理不同,Mixer 独立运行,因此它可以使用相当大的缓存和输出缓冲区,充当 Sidecar 的高度可伸缩且高度可用的二级缓存。 Mixer 旨在为每个实例提供高可用性。它的本地缓存和缓冲区可以减少延迟时间,还有助于屏蔽后端基础设施后端故障,即使后端没有响应也是如此。 3. Citadel Istio Citadel 安全功能提供强大的身份验证功能、强大的策略、透明的 TLS 加密以及用于保护服务和数据的身份验证、授权和审计(AAA)工具,Envoy 可以终止或向网格中的服务发起 TLS 流量。为此,Citadel 需要支持创建、签署和轮换证书。Istio Citadel 提供特定于应用程序的证书,可用于建立双向 TLS 以保护服务之间的流量。 借助 Istio Citadel,确保只能从经过严格身份验证和授权的客户端访问包含敏感数据的服务。Citadel 通过内置身份和凭证管理提供了强大的服务间和最终用户身份验证。可用于升级服务网格中未加密的流量,并为运维人员提供基于服务标识而不是网络控制的强制执行策略的能力。Istio 的配置策略在服务器端配置平台身份验证,但不在客户端强制实施该策略,同时允许你指定服务的身份验证要求。Istio 的密钥管理系统可自动生成、分发、轮换与撤销密钥和证书。 Istio RBAC 为 Istio 网格中的服务提供命名空间级别、服务级别和方法级别的访问权限控制,包括易于使用的基于角色的语义、服务到服务和最终用户到服务的授权,并在角色和角色绑定方面提供灵活的自定义属性支持。 Istio 可以增强微服务及其通信(包括服务到服务和最终用户到服务的通信)的安全性,且不需要更改服务代码。它为每个服务提供基于角色的强大身份机制,以实现跨集群、跨云端的交互操作。 4. Galley Galley 用于验证用户编写的 Istio API 配置。随着时间的推移,Galley 将接管 Istio 获取配置、处理和分配组件的顶级责任。它负责将其他的 Istio 组件与从底层平台(例如 Kubernetes)获取用户配置的细节中隔离开来。 总而言之,通过 Pilot,Istio 可在部署规模逐步扩大的过程中帮助你简化流量管理。通过 Mixer,借助强健且易于使用的监控功能,能够快速有效地检测和修复问题。通过 Citadel,减轻安全负担,让开发者可以专注于其他关键任务。 Istio 的架构设计中有几个关键目标,这些目标对于系统应对大规模流量和高性能的服务处理至关重要。 最大化透明度:要采用 Istio,应该让运维和开发人员只需付出很少的代价就可以从中获得实际价值。为此,Istio 将自身自动注入到服务间所有的网络路径中。Istio 使用 Envoy 代理来捕获流量,并且在可能的情况下自动对网络层进行编程,以便通过这些代理路由流量,而无需对已部署的应用程序代码进行太多的更改,甚至不需要任何更改。在 Kubernetes 中,Envoy 代理被注入到 pod 中,通过 iptables 规则来捕获流量。一旦注入 Envoy 代理到 pod 中并且修改路由规则,Istio 就能够调节所有流量。这个原则也适用于性能。当将 Istio 用于部署时,运维人员可以发现,为提供这些功能而增加的资源开销是很小的。所有组件和 API 在设计时都必须考虑性能和规模。 可扩展性:随着运维人员和开发人员越来越依赖 Istio 提供的功能,系统必然和他们的需求一起成长。在我们继续添加新功能的同时,最需要的是能够扩展策略系统,集成其他策略和控制来源,并将网格行为信号传播到其他系统进行分析。策略运行时支持标准扩展机制以便插入到其他服务中。此外,它允许扩展词汇表,以允许基于网格生成的新信号来强制执行策略。 可移植性:使用 Istio 的生态系统在很多方面都有所不同。Istio 必须能够以最少的代价运行在任何云或本地环境中。将基于 Istio 的服务移植到新环境应该是轻而易举的,而使用 Istio 将一个服务同时部署到多个环境中也是可行的,例如可以在混合云上部署以实现冗余灾备。 策略一致性:策略应用于服务之间的 API 调用,可以很好地控制网格行为。但对于无需在 API 级别表达的资源来说,对资源应用策略也同样重要。例如,将配额应用到机器学习训练任务消耗的 CPU 数量上,比将配额应用到启动这个工作的调用上更为有用。因此,Istio 将策略系统维护为具有自己的 API 的独特服务,而不是将其放到代理中,这允许服务根据需要直接与其集成。 剖析 Istio 数据平面 当介绍服务网格的概念时,提到了服务代理的概念以及如何使用代理构建一个服务网格,以调节和控制微服务之间的所有网络通信。Istio 使用 Envoy 代理作为默认的开箱即用服务代理,这些 Envoy 代理与参与服务网格的所有应用程序实例一起运行,但不在同一个容器进程中,形成了服务网格的数据平面。只要应用程序想要与其他服务通信,就会通过服务代理 Envoy 进行。由此可见,Envoy 代理是数据平面和整个服务网格架构中的关键组成部分。 1. Envoy 代理 Envoy 最初是由 Lyft 开发的,用于解决构建分布式系统时出现的一些复杂的网络问题。它于 2016 年 9 月作为开源项目提供,一年后加入了云原生计算基金会(CNCF)。Envoy 是用 C++ 语言实现的,具有很高的性能,更重要的是,它在高负载运行时也非常稳定和可靠。网络对应用程序来说应该是透明的,当网络和应用程序出现问题时,应该很容易确定问题的根源。正是基于这样的一种设计理念,将 Envoy 设计为一个面向服务架构的七层代理和通信总线。 为了更好地理解 Envoy,我们需要先搞清楚相关的几个基本术语: 进程外(Out of Process)架构:Envoy 是一个独立进程,Envoy 之间形成一个透明的通信网格,每个应用程序发送消息到本地主机或从本地主机接收消息,但无需关心网络拓扑。 单进程多线程模型:Envoy 使用了单进程多线程的架构模型。一个主线程管理各种琐碎的任务,而一些工作子线程则负责执行监听、过滤和转发功能。 下游(Downstream):连接到 Envoy 并发送请求、接收响应的主机叫下游主机,也就是说下游主机代表的是发送请求的主机。 上游(Upstream):与下游相对,接收请求的主机叫上游主机。 监听器(Listener):监听器是命名网络地址,包括端口、unix domain socket 等,可以被下游主机连接。Envoy 暴露一个或者多个监听器给下游主机连接。每个监听器都独立配置一些网络级别(即三层或四层)的过滤器。当监听器接收到新连接时,配置好的本地过滤器将被实例化,并开始处理后续事件。一般来说监听器架构用于执行绝大多数不同的代理任务,例如限速、TLS 客户端认证、HTTP 连接管理、MongoDB sniff?ing、原始 TCP 代理等。 集群(Cluster):集群是指 Envoy 连接的一组逻辑相同的上游主机。 xDS 协议:在 Envoy 中 xDS 协议代表的是多个发现服务协议,包括集群发现服务(CDS,Cluster Discovery Service)、监听器发现服务(LDS,Listener Discovery Service)、路由发现服务(RDS,Route Discovery Service)、端点发现服务(EDS,Endpoint Discovery Service),以及密钥发现服务(SDS,Secret Discovery Service)。 Envoy 代理有许多功能可用于服务间通信,例如,暴露一个或者多个监听器给下游主机连接,通过端口暴露给外部的应用程序;通过定义路由规则处理监听器中传输的流量,并将该流量定向到目标集群,等等。后续章节会进一步分析这几个发现服务在 Istio 中的角色和作用。 在了解了 Envoy 的术语之后,你可能想尽快知道 Envoy 到底起到了什么作用? 首先,Envoy 是一种代理,在网络体系架构中扮演着中介的角色,可以为网络中的流量管理添加额外的功能,包括提供安全性、隐私保护或策略等。在服务间调用的场景中,代理可以为客户端隐藏服务后端的拓扑细节,简化交互的复杂性,并保护后端服务不会过载。例如,后端服务实际上是运行的一组相同实例,每个实例能够处理一定量的负载。 其次,Envoy 中的集群(Cluster)本质上是指 Envoy 连接到的逻辑上相同的一组上游主机。那么客户端如何知道在与后端服务交互时要使用哪个实例或 IP 地址?Envoy 作为代理起到了路由选择的作用,通过服务发现(SDS,Service Discovery Service),Envoy 代理发现集群中的所有成员,然后通过主动健康检查来确定集群成员的健康状态,并根据健康状态,通过负载均衡策略决定将请求路由到哪个集群成员。而在 Envoy 代理处理跨服务实例的负载均衡过程中,客户端不需要知道实际部署的任何细节。 2. Envoy 的启动配置 Envoy 目前提供了两个版本的 API,即 v1 和 v2,从 Envoy 1.5.0 起就有 v2 API 了,为了能够让用户顺利地向 v2 版本 API 迁移,Envoy 启动的时候设置了一个参数--v2-conf?ig-only。通过这个参数,可以明确指定 Envoy 使用 v2 API 的协议。幸运的是,v2 API 是 v1 的一个超集,兼容 v1 的 API。在当前的 Istio 1.0 之后的版本中,明确指定了其支持 v2 的 API。通过查看使用 Envoy 作为 Sidecar 代理的容器启动命令,可以看到如下类似的启动参数,其中指定了参数--v2-config-only: $ /usr/local/bin/envoy -c /etc/istio/proxy/envoy-rev0.json --restart-epoch 0 --drain-time-s 45 --parent-shutdown-time-s 60 --service-cluster ratings --service-node sidecar~172.33.14.2~ratings-v1-8558d4458d-ld8x9.default~default.svc.cluster.local --max-obj-name-len 189 --allow-unknown-fields -l warn --v2-config-only 其中,参数 -c 表示的是基于版本 v2 的引导配置文件的路径,格式为 JSON,也支持其他格式,如 YAML、Proto3等。它会首先作为版本 v2 的引导配置文件进行解析,若解析失败,会根据 [--v2-conf?ig-only] 选项决定是否作为版本 v1 的 JSON 配置文件进行解析。其他参数解释如下,以便读者及时理解 Envoy 代理启动时的配置信息: restart-epoch 表示热重启周期,对于第一次启动默认为 0,每次热重启后都应该增加它。 service-cluster 定义 Envoy 运行的本地服务集群名称。 service-node 定义 Envoy 运行的本地服务节点名称。 drain-time-s 表示热重启期间 Envoy 将耗尽连接的时间(秒),默认为 600 秒(10 分钟)。通常耗尽时间应小于通过 --parent-shutdown-time-s 选项设置的父进程关闭时间。 parent-shutdown-time-s 表示 Envoy 在热重启时关闭父进程之前等待的时间(秒)。 max-obj-name-len 描述的是集群 cluster、路由配置 route_conf?ig 以及监听器 listener 中名称字段的最大长度,以字节为单位。此选项通常用于自动生成集群名称的场景,通常会超过 60 个字符的内部限制。默认为 60。 Envoy 的启动配置文件分为两种方式:静态配置和动态配置。具体表现为: 静态配置是将所有信息都放在配置文件中,启动的时候直接加载。 动态配置需要提供一个 Envoy 的服务端,用于动态生成 Envoy 需要的服务发现接口,也就是通常说的 xDS,通过发现服务来动态调整配置信息,Istio 实现了 v2 的 xDS API。 3. Envoy 静态与动态配置 Envoy 是由 JSON 或 YAML 格式的配置文件驱动的智能代理,对于已经熟悉 Envoy 或 Envoy 配置的用户来说,相信应该已经知道了 Envoy 的配置也有不同的版本。初始版本 v1 是 Envoy 启动时配置 Envoy 的原始方式。此版本已被弃用,以支持 Envoy 配置的 v2 版本。Envoy 的参考文档(https://www.envoyproxy.io/docs)还提供了明确区分 v1 和 v2 的文档。本文将只关注 v2 配置,因为它是最新的版本,也是 Istio 使用的版本。 Envoy 版本 v2 的配置 API 建立在 gRPC 之上,v2 API 的一个重要特性是可以在调用 API 时利用流功能来减少 Envoy 代理汇聚配置所需的时间。实际上,这也消除了轮询 API 的弊端,允许服务器将更新推送到 Envoy 代理,而不是定期轮询代理。 Envoy 的架构使得使用不同类型的配置管理方法成为可能。部署中采用的方法将取决于实现者的需求。简单部署可以通过全静态配置来实现,更复杂的部署可以递增地添加更复杂的动态配置。主要分为以下几种情况: 全静态:在全静态配置中,实现者提供一组监听器和过滤器链、集群和可选的 HTTP 路由配置。动态主机发现仅能通过基于 DNS 的服务发现。配置重载必须通过内置的热重启机制进行。 仅SDS/EDS:在静态配置之上,Envoy 可以通过该机制发现上游集群中的成员。 SDS/EDS 和 CDS:Envoy 可以通过该机制发现使用的上游集群。 SDS/EDS、CDS 和 RDS:RDS 可以在运行时发现用于 HTTP 连接管理器过滤器的整个路由配置。 SDS/EDS、CDS、RDS 和 LDS:LDS 可以在运行时发现整个监听器。这包括所有的过滤器堆栈,包括带有内嵌到 RDS 的应用的 HTTP 过滤器。 静态配置 我们可以使用 Envoy 的配置文件指定监听器、路由规则和集群。如下示例提供了一个非常简单的 Envoy 配置: static_resources: listeners: - name: httpbin-demo address: socket_address: { address: 0.0.0.0, port_value: 15001 } filter_chains: - filters: - name: envoy.http_connection_manager config: stat_prefix: egress_http route_config: name: httpbin_local_route virtual_hosts: - name: httpbin_local_service domains: ["*"] routes: - match: { prefix: "/" } route: auto_host_rewrite: true cluster: httpbin_service http_filters: - name: envoy.router clusters: - name: httpbin_service connect_timeout: 5s type: LOGICAL_DNS # Comment out the following line to test on v6 networks dns_lookup_family: V4_ONLY lb_policy: ROUND_ROBIN hosts: [{ socket_address: { address: httpbin, port_value: 8000 }}] 在这个简单的 Envoy 配置文件中,我们声明了一个监听器,它在端口 15001 上打开一个套接字并为其附加一个过滤器链。过滤器 http_connection_manager 在 Envoy 配置中使用路由指令(在此示例中看到的简单路由指令是匹配所有虚拟主机的通配符),并将所有流量路由到 httpbin_service 集群。配置的最后一部分定义了 httpbin_service 集群的连接属性。在此示例中,我们指定端点服务发现的类型为 LOGICAL_DNS、与上游 httpbin 服务通信时的负载均衡算法为 ROUND_ROBIN。 这是一个简单的配置文件,用于创建监听器传入的流量,并将所有流量路由到 httpbin 集群。它还指定要使用的负载均衡算法的设置以及要使用的连接超时配置。 你会注意到很多配置是明确指定的,例如指定了哪些监听器,路由规则是什么,我们可以路由到哪些集群等。这是完全静态配置文件的示例。 有关这些参数更多信息的解释,请参阅 Envoy 的文档(www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/service_discovery#logical-dns)。在前面的部分中,我们指出 Envoy 能够动态配置其各种设置。下面将介绍 Envoy 的动态配置以及 Envoy 如何使用 xDS API 进行动态配置。 动态配置 Envoy 可以利用一组 API 进行配置更新,而无需任何停机或重启。Envoy 只需要一个简单的引导配置文件,该配置文件将配置指向正确的发现服务 API,其余动态配置。Envoy 进行动态配置的 API 通常统称为 xDS 服务,具体包括如下: 监听器发现服务(LDS):一种允许 Envoy 查询整个监听器的机制,通过调用该 API 可以动态添加、修改或删除已知监听器;每个监听器都必须具有唯一的名称。如果未提供名称,Envoy 将创建一个 UUID。 路由发现服务(RDS):Envoy 动态获取路由配置的机制,路由配置包括 HTTP 标头修改、虚拟主机以及每个虚拟主机中包含的单个路由规则。每个 HTTP 连接管理器都可以通过 API 独立地获取自身的路由配置。RDS 配置隶属于监听器发现服务 LDS 的一部分,是 LDS 的一个子集,用于指定何时应使用静态和动态配置,以及指定使用哪个路由。 集群发现服务(CDS):一个可选的 API,Envoy 将调用该 API 来动态获取集群管理成员。Envoy 还将根据 API 响应协调集群管理,根据需要添加、修改或删除已知的集群。在 Envoy 配置中静态定义的任何集群都不能通过 CDS API 进行修改或删除。 端点发现服务(EDS):一种允许 Envoy 获取集群成员的机制,基于 gRPC 或 RESTJSON 的 API,它是 CDS 的一个子集;集群成员在 Envoy 术语中称为端点(Endpoint)。对于每个集群,Envoy 从发现服务获取端点。EDS 是首选的服务发现机制。 密钥发现服务(SDS):用于分发证书的 API;SDS 最重要的好处是简化证书管理。如果没有此功能,在 Kubernetes 部署中,必须将证书创建为密钥并挂载到 Envoy 代理容器中。如果证书过期,则需要更新密钥并且需要重新部署代理容器。使用密钥发现服务 SDS,那么 SDS 服务器会将证书推送到所有 Envoy 实例。如果证书过期,服务器只需将新证书推送到 Envoy 实例,Envoy 将立即使用新证书而无需重新部署。 聚合发现服务(ADS):上述其他 API 的所有更改的序列化流;你可以使用此单个 API 按顺序获取所有更改;ADS 并不是一个实际意义上的 xDS,它提供了一个汇聚的功能,在需要多个同步 xDS 访问的时候,ADS 可以在一个流中完成。配置可以使用上述服务中的一个或其中几个的组合,不必全部使用它们。需要注意的一点是,Envoy 的 xDS API 是建立在最终一致性的前提下,正确的配置最终会收敛。例如,Envoy 最终可能会使用新路由获取RDS的更新,该路由将流量路由到尚未在 CDS 中更新的集群。这意味着,路由可能会引入路由错误,直到更新 CDS。Envoy 引入了聚合发现服务 ADS 来解决这种问题,而 Istio 实现了聚合发现服务 ADS,并使用 ADS 进行代理配置的更改。 例如,Envoy 代理要动态发现监听器,可以使用如下配置: dynamic_resources: lds_config: api_config_source: api_type: GRPC grpc_services: - envoy_grpc: cluster_name: xds_cluster clusters: - name: xds_cluster connect_timeout: 0.25s type: STATIC lb_policy: ROUND_ROBIN http2_protocol_options: {} hosts: [{ socket_address: { address: 127.0.0.3, port_value: 5678 }}] 通过上面的配置,我们不需要在配置文件中显式配置每个监听器。我们告诉 Envoy 使用 LDS API 在运行时发现正确的监听器配置值。但是,我们需要明确配置一个集群,这个集群就是 LDS API 所在的位置,也就是该示例中定义的集群 xds_cluster。 在静态配置的基础上,比较直观地表示出各个发现服务所提供的信息。在静态配置的基础上,比较直观地表示出各个发现服务所提供的信息。 本文摘自于《Istio 服务网格解析与实战》,经出版方授权发布。本书由阿里云高级技术专家王夕宁撰写,详细介绍 Istio 的基本原理与开发实战,包含大量精选案例和参考代码可以下载,可快速入门 Istio 开发。Gartner 认为,2020 年服务网格将成为所有领先的容器管理系统的标配技术。本书适合所有对微服务和云原生感兴趣的读者,推荐大家对本书进行深入的阅读。
通过服务网格 ASM,可以将一个应用的服务组件部署在同 VPC 的多个集群上。本文以 Bookinfo 应用为例,介绍如何将该应用部署到包含两个集群的 ASM 实例。 欢迎扫码入群进一步交流: 前提条件 在同一 VPC 下已创建两个 ACK 集群(本例中 asm-zjk-prod3-c1 和 asm-zjk-prod3-c2),详情参见创建 Kubernetes 集群。 已创建一个 ASM 实例(本例中为 asm-zjk-prod3),详情参见创建 ASM 实例。 步骤一:修改集群的安全组名称 将两个集群对应的安全组名称修改为易于辨识的名称,本例中为 asm-zjk-prod3-c1-sg和asm-zjk-prod3-c2-sg。 登录ECS管理控制台。 在左侧导航栏,单击网络与安全 > 安全组。 在顶部状态栏左上角处,选择地域。 在安全组列表页面中,找到需要修改的安全组,单击操作列下的修改。 在弹出的对话框中,修改安全组名称和描述。单击确定。 修改后的名称,如下图所示。 步骤二:配置集群的互访联通性 为了使两个集群能够互相访问,需要为彼此添加安全组访问规则。 在 asm-zjk-prod3-c1-sg 安全组配置界面,添加以 asm-zjk-prod3-c2-sg 为授权对象的访问规则。详情参见添加安全组规则。 添加规则之后的结果,如图所示: 同样地,在 asm-zjk-prod3-c2-sg 安全组规则配置界面,添加以 asm-zjk-prod3-c1-sg 为授权对象的访问规则。 步骤三:添加集群到 ASM 实例并部署集群的入口网关 将两个集群添加到 ASM 实例后,由于两个集群已实现访问互通,因此只需为一个集群部署入口网关。 将两个集群添加到 ASM 实例,详情参见添加集群到 ASM 实例。 在其中一个集群中部署入口网关,详情参见添加入口网关。 步骤四:部署 Bookinfo 应用 为了体验ASM 跨集群的应用部署能力,Bookinfo 应用的不同微服务分别被部署在两个集群上。 在 第二个集群asm-zjk-prod3-c1 中部署不包含 review-v3 deployment 的 Bookinfo 应用,详情参见部署应用到 ASM 实例。 说明: Review-v3 deployment 对应的功能是书评中显示红色星。 对应的 Yaml 文件内容如下所示: apiVersion: v1 kind: Service metadata: name: details labels: app: details service: details spec: ports: - port: 9080 name: http selector: app: details --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-details labels: account: details --- apiVersion: apps/v1 kind: Deployment metadata: name: details-v1 labels: app: details version: v1 spec: replicas: 1 selector: matchLabels: app: details version: v1 template: metadata: labels: app: details version: v1 spec: serviceAccountName: bookinfo-details containers: - name: details image: docker.io/istio/examples-bookinfo-details-v1:1.15.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 --- apiVersion: v1 kind: Service metadata: name: ratings labels: app: ratings service: ratings spec: ports: - port: 9080 name: http selector: app: ratings --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-ratings labels: account: ratings --- apiVersion: apps/v1 kind: Deployment metadata: name: ratings-v1 labels: app: ratings version: v1 spec: replicas: 1 selector: matchLabels: app: ratings version: v1 template: metadata: labels: app: ratings version: v1 spec: serviceAccountName: bookinfo-ratings containers: - name: ratings image: docker.io/istio/examples-bookinfo-ratings-v1:1.15.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 --- apiVersion: v1 kind: Service metadata: name: reviews labels: app: reviews service: reviews spec: ports: - port: 9080 name: http selector: app: reviews --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-reviews labels: account: reviews --- apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v1 labels: app: reviews version: v1 spec: replicas: 1 selector: matchLabels: app: reviews version: v1 template: metadata: labels: app: reviews version: v1 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v1:1.15.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 --- apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v2 labels: app: reviews version: v2 spec: replicas: 1 selector: matchLabels: app: reviews version: v2 template: metadata: labels: app: reviews version: v2 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v2:1.15.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 --- apiVersion: v1 kind: Service metadata: name: productpage labels: app: productpage service: productpage spec: ports: - port: 9080 name: http selector: app: productpage --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-productpage labels: account: productpage --- apiVersion: apps/v1 kind: Deployment metadata: name: productpage-v1 labels: app: productpage version: v1 spec: replicas: 1 selector: matchLabels: app: productpage version: v1 template: metadata: labels: app: productpage version: v1 spec: serviceAccountName: bookinfo-productpage containers: - name: productpage image: docker.io/istio/examples-bookinfo-productpage-v1:1.15.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 --- 在集群asm-zjk-prod3-c2 中部署 review-v3 以及 rating service(review 依赖的服务)。 对应的 YAML文件内容如下所示: ################################################################################################## # Reviews service ################################################################################################## apiVersion: v1 kind: Service metadata: name: reviews labels: app: reviews service: reviews spec: ports: - port: 9080 name: http selector: app: reviews --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-reviews labels: account: reviews --- apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v3 labels: app: reviews version: v3 spec: replicas: 1 selector: matchLabels: app: reviews version: v3 template: metadata: labels: app: reviews version: v3 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v3:1.15.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 --- ################################################################################################## # Ratings service ################################################################################################## apiVersion: v1 kind: Service metadata: name: ratings labels: app: ratings service: ratings spec: ports: - port: 9080 name: http selector: app: ratings 步骤五:添加虚拟服务和 Istio 服务网关 在 ASM 实例的 default 命名空间下新建一个虚拟服务,名为 bookinfo,详情参见管理 Istio 资源定义。对应的 Yaml 文件内容如下所示: apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: bookinfo spec: hosts: - "*" gateways: - bookinfo-gateway http: - match: - uri: exact: /productpage - uri: prefix: /static - uri: exact: /login - uri: exact: /logout - uri: prefix: /api/v1/products route: - destination: host: productpage port: number: 9080 在 ASM 实例的 default 命名空间下新建一个 Istio 网关,名为 bookinfo-gateway,详情参见管理 Istio 资源定义。对应的 Yaml 文件内容如下所示: apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: bookinfo-gateway spec: selector: istio: ingressgateway # use istio default controller servers: - port: number: 80 name: http protocol: HTTP hosts: - "*" 访问 productpage 页面,刷新页面时会轮流显示 reviews 的 3 个版本。虽然 review-v3 和其他服务不在同一个集群中,也可以正常显示。 步骤六:修改流量路由规则 通过定义目标规则和虚拟服务,可以定义 Bookinfo 应用的微服务部署策略。本例中将指定 Bookinfo 总是使用 review v3 版本。 在 ASM 实例的 default 命名空间下新建一个目标规则,名为 reviews。Yaml 文件的内容如下所示: apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: reviews spec: host: reviews subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 - name: v3 labels: version: v3 在 ASM 实例的 default 命名空间下新建一个虚拟服务,名为 reviews。对应的 Yaml 文件内容如下所示: apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: reviews spec: hosts: - reviews http: - route: - destination: host: reviews subset: v2 weight: 50 - destination: host: reviews subset: v3 weight: 50 此时访问 productpage 页面,reviews 将按照50:50的比例使用 v2和v3 版本,即书评中为黑色和红色星。
本系列文章讲讲述阿里云服务网格ASM的一些扩展能力: 阿里云服务网格ASM之扩展能力(1):在ASM中通过EnvoyFilter添加HTTP请求头 阿里云服务网格ASM之扩展能力(2):在ASM中支持自定义外部授权 阿里云服务网格ASM之扩展能力(3):在ASM中使用开放策略代理OPA 阿里云服务网格ASM之扩展能力(4):在ASM中实现分布式跟踪 欢迎扫码入群进一步交流: 服务网格与分布式跟踪 根据Open Tracing网站的介绍,分布式跟踪(也称为分布式请求跟踪)是一种用于对应用程序进行概要分析和监视的方法,尤其是针对使用微服务架构构建的应用程序。分布式跟踪有助于查明哪里发生故障以及什么原因导致性能下降。常见的误解是在使用服务网格进行服务跟踪实现时,不需要任何代码的更改。实际上,根据Istio的官方介绍,尽管Istio代理能够自动发送Span,但是应用程序仍然需要传播适当的HTTP标头,以便在代理发送Span信息时,可以将Span正确地关联到单个跟踪中。为此,应用程序需要收集以下标头并将其从传入请求传播到任何传出请求。 x-request-id x-b3-traceid x-b3-spanid x-b3-parentspanid x-b3-sampled x-b3-flags x-ot-span-context 该x-b3头起源于Zipkin项目,标头的B3部分是以Zipkin的原始名称BigBrotherBird命名。在服务调用之间传递这些标头称之为B3传播。根据Zipkin的原理,这些属性通常通过HTTP标头进行传播,并最终传播到下游,以确保将源自同一根的所有活动收集在一起。 阿里云链路追踪服务 阿里云链路追踪 Tracing Analysis 为分布式应用的开发者提供了完整的调用链路还原、调用请求量统计、链路拓扑、应用依赖分析等工具,可以帮助开发者快速分析和诊断分布式应用架构下的性能瓶颈,提高微服务时代下的开发诊断效率。使用链路追踪之前,首先需要开通链路追踪服务。由于链路追踪依赖日志服务 LOG 和访问控制 RAM 服务,所以也需要开通这两项服务,并授权链路追踪读写您的日志服务数据。关于开通相关服务和授权的方法,请参见开通相关服务并授权。 为ASM实例启用链路追踪 服务网格ASM集成了阿里云链路追踪服务,为分布式应用的开发者提供了完整的调用链路还原、调用请求量统计、链路拓扑、应用依赖分析等能力,可以帮助开发者快速分析和诊断分布式应用架构下的性能瓶颈,提升开发诊断效率。 在创建ASM实例时启用链路追踪 用户在创建网格时,可以通过勾选“启用链路追踪”来启用ASM的链路追踪能力,在勾选后,还用户还可以设置追踪流量的百分比,设置范围为0.01 - 100.00。 说明:启用该配置时,用户需要提前开通阿里云链路追踪服务。 为已有ASM实例启用链路追踪或调整追踪比例 用户可以随时开启ASM实例的追踪能力或调整链路追踪的百分比。 如果在创建服务网格ASM实例时没有勾选启用OPA插件,可以通过如下方式重启开启。 登录服务网格控制台,在ASM实例详情页的右上角,点击功能设置按钮。 在弹出的窗口中,可以重新勾选启用或者禁用链路追踪(当然,如果启用的话,仍然是需要确保已经开通了链路服务)。 部署示例 按照文档部署应用到一个ASM服务网格中。查看示例中的以Python 语言实现的productpage服务,则会发现该应用程序使用了OpenTracing库从HTTP请求中提取了所需的标头: def getForwardHeaders(request): headers = {} # x-b3-*** headers can be populated using the opentracing span span = get_current_span() carrier = {} tracer.inject( span_context=span.context, format=Format.HTTP_HEADERS, carrier=carrier) headers.update(carrier) # ... incoming_headers = ['x-request-id'] # ... for ihdr in incoming_headers: val = request.headers.get(ihdr) if val is not None: headers[ihdr] = val return headers 同样地,查看以Java语言实现的reviews服务: @GET @Path("/reviews/{productId}") public Response bookReviewsById(@PathParam("productId") int productId, @HeaderParam("end-user") String user, @HeaderParam("x-request-id") String xreq, @HeaderParam("x-b3-traceid") String xtraceid, @HeaderParam("x-b3-spanid") String xspanid, @HeaderParam("x-b3-parentspanid") String xparentspanid, @HeaderParam("x-b3-sampled") String xsampled, @HeaderParam("x-b3-flags") String xflags, @HeaderParam("x-ot-span-context") String xotspan) { if (ratings_enabled) { JsonObject ratingsResponse = getRatings(Integer.toString(productId), user, xreq, xtraceid, xspanid, xparentspanid, xsampled, xflags, xotspan); 访问示例 在浏览器地址栏输入_http://{__入口网关服务的IP地址__}/__productpage_,可以看到如下类似页面,刷新页面以实现多次访问效果。 查看应用列表 应用列表页面展示了所有被监控应用的健康度得分、本日请求数、本日错误数等关键指标。您还可以为应用设置自定义标签并使用标签来筛选。 请按照以下步骤进入应用列表页面。 登录链路追踪 Tracing Analysis 控制台。 在左侧导航栏中单击应用列表,并在应用列表页面顶部选择目标地域。 查看应用详情 应用详情页面可展示应用在所部属的每一台机器上的关键性能指标、调用拓扑图和调用链路。 登录链路追踪 Tracing Analysis 控制台。 在左侧导航栏中单击应用列表,并在应用列表页面顶部选择地域,然后单击应用名称。 在左侧导航栏中单击应用详情,在左侧的机器列表中单击全部或一台机器,然后在概览页签上查看调用拓扑图和关键性能指标。应用详情页面的调用链路页签列出了该应用在所选机器上耗时最长的 100 个调用链路。 查看调用链瀑布图 在调用链路页签上单击 TraceID,即可在新窗口中打开调用链路页面,并查看该调用链路的瀑布图。 在新窗口中的调用链路页面上,您可以看到调用链路的日志产生时间、状态、IP 地址/机器名称、服务名、时间轴等信息。 注意: IP 地址字段显示的是 IP 地址还是机器名称,取决于应用设置页面上的显示配置。详情请参见管理应用和标签。 将鼠标悬浮于服务名上,还可以查看该服务的时长、开始时间、Tag 和日志事件等信息。 其他具体操作可以参考链路跟踪文档。
本系列文章讲讲述阿里云服务网格ASM的一些扩展能力: 阿里云服务网格ASM之扩展能力(1):在ASM中通过EnvoyFilter添加HTTP请求头 阿里云服务网格ASM之扩展能力(2):在ASM中支持自定义外部授权 阿里云服务网格ASM之扩展能力(3):在ASM中使用开放策略代理OPA 阿里云服务网格ASM之扩展能力(4):在ASM中实现分布式跟踪 欢迎扫码入群进一步交流: 前面的系列文档中介绍了如何创建服务网格ASM实例,并介绍了如何将一个应用示例部署到 ASM 实例中,本文在此基础上介绍如何在ASM中使用开放策略代理OPA定义细粒度访问控制。 前提条件 已创建至少一个 ASM 实例,并已添加至少一个 ACK 集群到该实例中。 已通过 Kubernetes 命令行客户端 kubectl 连接到 ASM 实例中新添加的 ACK 集群,详情参见通过 kubectl 连接 Kubernetes 集群。 已通过 Kubernetes 命令行客户端 kubectl 连接到 ASM 实例,详情参见通过 kubectl 连接 ASM 实例。 开放策略代理OPA 作为由CNCF托管的一个孵化项目,开放策略代理(OPA)是一个策略引擎,可用于为您的应用程序实现细粒度的访问控制。例如,可以使用OPA 跨微服务实现授权等。如图所示,OPA作为通用策略引擎,可以与微服务一起部署为独立服务。为了保护应用程序,必须先授权对微服务的每个请求,然后才能对其进行处理。为了检查授权,微服务对OPA进行API调用,以确定请求是否被授权。 在ASM中启用OPA 服务网格ASM集成了开放策略代理OPA,可用于为您的应用程序实现细粒度的访问控制。例如,可以使用OPA 跨微服务实现授权等。启用后,如同Istio Envoy代理容器一样,OPA代理容器也会随之被注入到业务Pod中。然后,在ASM中就可以使用OPA定义访问控制策略,为分布式应用的开发者提供了开箱可用的能力,可以帮助开发者快速定义使用策略,提升开发效率。 其中,如图所示在创建服务网格实例时,可以通过设置是否启用OPA插件。 功能设置 如果在创建服务网格ASM实例时没有勾选启用OPA插件,可以通过如下方式重启开启。 登录服务网格控制台,在ASM实例详情页的右上角,点击功能设置按钮。 在弹出的窗口中,可以重新勾选启用OPA插件。 注意:部署业务Pod之前,必须确保已经部署了OPA配置文件和策略的配置项Configmap,具体如下。 部署OPA配置 部署OPA配置文件。通过 kubectl 连接到 ASM 实例中新添加的 ACK 集群,执行如下命令: kubectl apply -n {替换成实际的namespace} -f - <<EOF apiVersion: v1 kind: ConfigMap metadata: name: opa-istio-config data: config.yaml: | plugins: envoy_ext_authz_grpc: addr: :9191 path: istio/authz/allow EOF 部署OPA策略 当前在ASM中已经支持使用Rego定义的OPA策略,后续会支持基于WebAssembly的OPA扩展能力。 通过 kubectl 连接到 ASM 实例中新添加的 ACK 集群,替换成实际的策略定义,执行如下命令: kubectl apply -n {替换成实际的namespace} -f - <<EOF apiVersion: v1 kind: ConfigMap metadata: name: opa-policy data: policy.rego: | ###以下为示例策略定义,需要替换成实际的策略定义 package istio.authz import input.attributes.request.http as http_request default allow = false allow { roles_for_user[r] required_roles[r] } roles_for_user[r] { r := user_roles[user_name][_] } required_roles[r] { perm := role_perms[r][_] perm.method = http_request.method perm.path = http_request.path } user_name = parsed { [_, encoded] := split(http_request.headers.authorization, " ") [parsed, _] := split(base64url.decode(encoded), ":") } user_roles = { "guest1": ["guest"], "admin1": ["admin"] } role_perms = { "guest": [ {"method": "GET", "path": "/productpage"}, ], "admin": [ {"method": "GET", "path": "/productpage"}, {"method": "GET", "path": "/api/v1/products"}, ], } EOF 注入OPA代理容器 按照部署应用到 ASM 实例重新部署示例应用到 ASM 实例,并定义相应的Istio虚拟服务和入口网关,详情参见管理 Istio 资源定义。 登录容器服务管理控制台,单击左侧导航栏中的应用->容器组; 在右侧打开的页面中,选择对应的集群及命名空间(如default),此时Bookinfo应用Pod应为运行中,并且每一个Pod内都被注入了Sidecar代理(istio-proxy)和OPA代理(opa-istio),类似如下图所示: 执行结果 上述策略限制对BookInfo的访问,定义如下: guest1被授予guest角色,并且可以访问/productpage但不能访问/v1/api/products。 admin1被授予admin角色,并且可以访问/productpage和/v1/api/products。 curl -i --user guest1:password http://{入口网关服务的IP地址}/productpage HTTP/1.1 200 OK ...... curl -i --user guest1:password http://{入口网关服务的IP地址}/api/v1/products HTTP/1.1 403 Forbidden ...... curl -i --user admin1:password http://{入口网关服务的IP地址}/productpage HTTP/1.1 200 OK ...... curl -i --user admin1:password http://{入口网关服务的IP地址}/api/v1/products HTTP/1.1 200 OK ......
本系列文章讲讲述阿里云服务网格ASM的一些扩展能力: 阿里云服务网格ASM之扩展能力(1):在ASM中通过EnvoyFilter添加HTTP请求头 阿里云服务网格ASM之扩展能力(2):在ASM中支持自定义外部授权 阿里云服务网格ASM之扩展能力(3):在ASM中使用开放策略代理OPA 阿里云服务网格ASM之扩展能力(4):在ASM中实现分布式跟踪 欢迎扫码入群进一步交流: 背景信息 服务网格中服务间存在着调用请求,这些请求的授权决定可以由运行在网格外部的gRPC服务处理。外部授权过滤器调用授权服务以检查传入请求是否被授权。如果在过滤器中将该请求视为未授权,则该请求将被403(禁止)响应拒绝。 建议将这些授权过滤器配置为过滤器链中的第一个过滤器,以便在其余过滤器处理请求之前对请求进行授权。 关于Envoy的外部授权的其他内容,可以参见External Authorization。 gRPC外部服务需要相应的接口,实现该Check()方法。具体来说,external_auth.proto 定义了请求响应上下文: // A generic interface for performing authorization check on incoming // requests to a networked service. service Authorization { // Performs authorization check based on the attributes associated with the // incoming request, and returns status `OK` or not `OK`. rpc Check(v2.CheckRequest) returns (v2.CheckResponse); } 实现外部授权服务 基于上述gRPC服务接口定义,实现示例外部授权服务如下,在Check()方法中判断Bearer Token值是否以asm-开头。事实上,只要符合该接口定义,可以添加更为复杂的处理逻辑进行检查。 package main import ( "context" "log" "net" "strings" "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" auth "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2" envoy_type "github.com/envoyproxy/go-control-plane/envoy/type" "github.com/gogo/googleapis/google/rpc" "google.golang.org/grpc" ) // empty struct because this isn't a fancy example type AuthorizationServer struct{} // inject a header that can be used for future rate limiting func (a *AuthorizationServer) Check(ctx context.Context, req *auth.CheckRequest) (*auth.CheckResponse, error) { authHeader, ok := req.Attributes.Request.Http.Headers["authorization"] var splitToken []string if ok { splitToken = strings.Split(authHeader, "Bearer ") } if len(splitToken) == 2 { token := splitToken[1] // Normally this is where you'd go check with the system that knows if it's a valid token. if strings.HasPrefix(token, "asm-") { return &auth.CheckResponse{ Status: &rpc.Status{ Code: int32(rpc.OK), }, HttpResponse: &auth.CheckResponse_OkResponse{ OkResponse: &auth.OkHttpResponse{ Headers: []*core.HeaderValueOption{ { Header: &core.HeaderValue{ Key: "x-custom-header-from-authz", Value: "some value", }, }, }, }, }, }, nil } } return &auth.CheckResponse{ Status: &rpc.Status{ Code: int32(rpc.UNAUTHENTICATED), }, HttpResponse: &auth.CheckResponse_DeniedResponse{ DeniedResponse: &auth.DeniedHttpResponse{ Status: &envoy_type.HttpStatus{ Code: envoy_type.StatusCode_Unauthorized, }, Body: "Need an Authorization Header with a character bearer token using asm- as prefix!", }, }, }, nil } func main() { // create a TCP listener on port 4000 lis, err := net.Listen("tcp", ":4000") if err != nil { log.Fatalf("failed to listen: %v", err) } log.Printf("listening on %s", lis.Addr()) grpcServer := grpc.NewServer() authServer := &AuthorizationServer{} auth.RegisterAuthorizationServer(grpcServer, authServer) if err := grpcServer.Serve(lis); err != nil { log.Fatalf("Failed to start server: %v", err) } } 可以直接使用镜像: registry.cn-beijing.aliyuncs.com/istio-samples/ext-authz-grpc-service:latest 或者可以基于以下Dockerfile构建镜像,具体代码参见istio_ext_authz_filter_sample。 启动外部授权服务器 从 Github项目库中下载示例部署YAML 文件。 或者复制以下YAML定义: apiVersion: v1 kind: Service metadata: name: extauth-grpc-service spec: ports: - port: 4000 targetPort: 4000 protocol: TCP name: grpc selector: app: extauth-grpc-service type: ClusterIP --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: extauth-grpc-service spec: replicas: 1 template: metadata: labels: app: extauth-grpc-service spec: containers: - name: extauth image: registry.cn-beijing.aliyuncs.com/istio-samples/ext-authz-grpc-service:latest ports: - containerPort: 4000 通过 kubectl 连接到 ASM 实例中新添加的 ACK 集群,执行如下命令: kubectl apply -n istio-system -f extauth-sample-grpc-service.yaml 将看到以下输出显示已成功部署: service/extauth-grpc-service created deployment.extensions/extauth-grpc-service created 等待部署的外部授权pod启动之后,接下来开始部署示例应用。 部署示例应用 该示例部署使用了命名空间default,并且已启动Sidecar自动注入。 从 Github项目库中下载示例部署示例httpbin服务的YAML 文件。 通过 kubectl 连接到 ASM 实例中新添加的 ACK 集群,执行如下命令: kubectl apply -f httpbin.yaml 然后部署用于测试的客户端示例应用sleep。从 Github项目库中下载示例部署示例sleep服务的YAML 文件。 通过 kubectl 连接到 ASM 实例中新添加的 ACK 集群,执行如下命令: kubectl apply -f sleep.yaml 定义EnvoyFilter 在控制平面区域,选择EnvoyFilter页签,然后单击新建。 在新建页面中,选择相应的命名空间。本例中选择的命名空间为 default。 在文本框中,定义EnvoyFilter,可以复制粘贴EnvoyFilter定义到编辑框中。可参考如下 YAML 定义: apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: # This needs adjusted to be the app name name: extauth-sample spec: workloadSelector: labels: # This needs adjusted to be the app name app: httpbin # Patch the envoy configuration configPatches: # Adds the ext_authz HTTP filter for the ext_authz API - applyTo: HTTP_FILTER match: context: SIDECAR_INBOUND listener: name: virtualInbound filterChain: filter: name: "envoy.http_connection_manager" patch: operation: INSERT_BEFORE value: # Configure the envoy.ext_authz here: name: envoy.ext_authz config: grpc_service: # NOTE: *SHOULD* use envoy_grpc as ext_authz can use dynamic clusters and has connection pooling google_grpc: target_uri: extauth-grpc-service.istio-system:4000 stat_prefix: ext_authz timeout: 0.2s failure_mode_allow: false with_request_body: max_request_bytes: 8192 allow_partial_message: true 单击确定,将会看到EnvoyFilter已成功创建。 验证外部授权 登录到Sleep Pod容器中执行如下命令: export SLEEP_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) kubectl exec -it $SLEEP_POD -c sleep -- sh -c 'curl http://httpbin:8000/headers' 返回如下结果: Need an Authorization Header with a character bearer token using asm- as prefix! 可以看到示例应用程序的请求没有通过外部授权的许可,原因是请求头中并没有满足Bearer Token值以asm-开头。 在请求中添加以asm-开头的Bearer Token请求头,再次执行如下命令: export SLEEP_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) kubectl exec -it $SLEEP_POD -c sleep -- sh -c 'curl -H "Authorization: Bearer asm-token1" http://httpbin:8000/headers' 返回如下结果: { "headers": { "Accept": "*/*", "Authorization": "Bearer asm-token1", "Content-Length": "0", "Host": "httpbin:8000", "User-Agent": "curl/7.64.0", "X-B3-Parentspanid": "dab85d9201369071", "X-B3-Sampled": "1", "X-B3-Spanid": "c29b18886e98a95f", "X-B3-Traceid": "66875d955ac13dfcdab85d9201369071", "X-Custom-Header-From-Authz": "some value" } } 可以看到示例应用程序的请求通过外部授权的许可。
本系列文章讲讲述阿里云服务网格ASM的一些扩展能力: 阿里云服务网格ASM之扩展能力(1):在ASM中通过EnvoyFilter添加HTTP请求头 阿里云服务网格ASM之扩展能力(2):在ASM中支持自定义外部授权 阿里云服务网格ASM之扩展能力(3):在ASM中使用开放策略代理OPA 阿里云服务网格ASM之扩展能力(4):在ASM中实现分布式跟踪 欢迎扫码入群进一步交流: 背景信息 安全的HTTP请求头可以支持以非常简单的方式提高Web应用程序的安全性。OWASP提供了最佳实践指南和编程框架,描述了如何使用安全请求头保护应用程序的安全,包括了如下的基准设置: HTTP头 默认安全 描述 Content-Security-Policy frame-ancestors none; 防止其他网站进行Clickjacking攻击 X-XSS-Protection 1; mode=block 激活浏览器的XSS过滤器(如果可用);检测到XSS时阻止渲染 X-Content-Type-Options Nosniff 禁用浏览器的内容类型嗅探 Referrer-Policy no-referrer 禁用自动发送引荐来源请求头 X-Download-Options noopen 禁用旧版本IE中的自动打开下载功能 X-DNS-Prefetch-Control off 对页面上的外部链接禁用推测性DNS解析 Server envoy 由Istio的入口网关自动设置 X-Powered-by 去掉该值,以隐藏潜在易受攻击的应用程序服务器的名称和版本 Feature-Policy camera ‘none’;microphone ‘none’;geolocation ‘none’;encrypted-media ‘none’;payment ‘none’;speaker ‘none’;usb ‘none’; 控制可以在浏览器中使用的功能和API 通过curl命令可以看到Bookinfo示例应用程序的HTTP请求头信息,如下所示: curl -I http://{入口网关服务的IP地址}/productpage HTTP/1.1 200 OK content-type: text/html; charset=utf-8 content-length: 5183 server: istio-envoy date: Tue, 28 Jan 2020 08:15:21 GMT x-envoy-upstream-service-time: 28 可以看到默认情况下,示例应用程序的入口首页请求并没有包含上述安全相关的HTTP请求头。 接下来,将会介绍如何在ASM中通过Istio EnvoyFilter添加安全的HTTP请求头。 定义EnvoyFilter 如果还没有建立 kubectl 命令行客户端与 ASM 实例的连接,请参见通过 kubectl 连接 ASM 实例进行配置。 部署Istio EnvoyFilter,执行如下命令: apply -f - <apiVersion: networking.istio.io/v1alpha3kind: EnvoyFiltermetadata:name: security-by-default-header-filterspec:filters: listenerMatch: listenerType: GATEWAY filterType: HTTP filterName: envoy.lua filterConfig: inlineCode: | function envoy_on_response(response_handle) function hasFrameAncestors(rh) s = rh:headers():get("Content-Security-Policy"); delimiter = ";"; defined = false; for match in (s..delimiter):gmatch("(.-)"..delimiter) do match = match:gsub("%s+", ""); if match:sub(1, 15)=="frame-ancestors" then return true; end end return false; end if not response_handle:headers():get("Content-Security-Policy") then csp = "frame-ancestors none;"; response_handle:headers():add("Content-Security-Policy", csp); elseif response_handle:headers():get("Content-Security-Policy") then if not hasFrameAncestors(response_handle) then csp = response_handle:headers():get("Content-Security-Policy"); csp = csp .. ";frame-ancestors none;"; response_handle:headers():replace("Content-Security-Policy", csp); end end if not response_handle:headers():get("X-Frame-Options") then response_handle:headers():add("X-Frame-Options", "deny"); end if not response_handle:headers():get("X-XSS-Protection") then response_handle:headers():add("X-XSS-Protection", "1; mode=block"); end if not response_handle:headers():get("X-Content-Type-Options") then response_handle:headers():add("X-Content-Type-Options", "nosniff"); end if not response_handle:headers():get("Referrer-Policy") then response_handle:headers():add("Referrer-Policy", "no-referrer"); end if not response_handle:headers():get("X-Download-Options") then response_handle:headers():add("X-Download-Options", "noopen"); end if not response_handle:headers():get("X-DNS-Prefetch-Control") then response_handle:headers():add("X-DNS-Prefetch-Control", "off"); end if not response_handle:headers():get("Feature-Policy") then response_handle:headers():add("Feature-Policy", "camera 'none';".. "microphone 'none';".. "geolocation 'none';".. "encrypted-media 'none';".. "payment 'none';".. "speaker 'none';".. "usb 'none';"); end if response_handle:headers():get("X-Powered-By") then response_handle:headers():remove("X-Powered-By"); end end EOF 将看到以下输出显示过滤器已成功部署: envoyfilter.networking.istio.io/security-by-default-header-filter created 验证HTTP请求头 通过curl命令确认添加了安全HTTP请求头,执行如下: curl -I http://{入口网关服务的IP地址}/productpage HTTP/1.1 200 OK content-type: text/html; charset=utf-8 content-length: 4183 server: istio-envoy date: Tue, 28 Jan 2020 09:07:01 GMT x-envoy-upstream-service-time: 17 content-security-policy: frame-ancestors none; x-frame-options: deny x-xss-protection: 1; mode=block x-content-type-options: nosniff referrer-policy: no-referrer x-download-options: noopen x-dns-prefetch-control: off feature-policy: camera 'none';microphone 'none';geolocation 'none';encrypted-media 'none';payment 'none';speaker 'none';usb 'none'; 可以看到示例应用程序的入口首页请求已经包含了上述介绍过的安全相关的HTTP请求头。 由此可见,在ASM中可以使用EnvoyFilter以非常简单的方式添加HTTP请求头。
通过管理 Istio 资源定义,可以实现 ASM 服务网格的流量治理、鉴权以及安全保护等能力。本文介绍如何定义虚拟服务和目标规则这两种 Istio 资源。 阿里云服务网格ASM公测来袭系列之一:快速了解什么是ASM 阿里云服务网格ASM公测来袭系列之二:快速创建一个服务网格ASM实例 阿里云服务网格ASM公测来袭系列之三:添加一个ACK集群到服务网格ASM实例 阿里云服务网格ASM公测来袭系列之四:添加入口网关 阿里云服务网格ASM公测来袭系列之五:部署应用到ASM的数据面集群中 阿里云服务网格ASM公测来袭系列之六:在ASM中为应用定义路由规则 欢迎扫码入群进一步交流: 定义虚拟服务 在控制平面区域,选择虚拟服务页签,然后单击新建。 在新建页面,定义虚拟服务,单击确定。 选择相应的命名空间。本例中选择的命名空间为 default。 在文本框中,定义 Istio 虚拟服务。可参考如下 YAML 定义: apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: bookinfo spec: hosts: - "*" gateways: - bookinfo-gateway http: - match: - uri: exact: /productpage - uri: prefix: /static - uri: exact: /login - uri: exact: /logout - uri: prefix: /api/v1/products route: - destination: host: productpage port: number: 9080 在虚拟服务页面可以看到新建的 bookinfo 服务。 定义 Istio 网关 在控制平面区域,选择Istio网关页签,然后单击新建。 在新建页面中,定义虚拟服务,单击确定。 选择相应的命名空间。本例中选择的命名空间为 default。 在文本框中,定义 Istio 网关。可参考如下 YAML 定义: apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: bookinfo-gateway spec: selector: istio: ingressgateway # use istio default controller servers: - port: number: 80 name: http protocol: HTTP hosts: - "*" 在Istio服务网关页面可以看到新建的 bookinfo-gateway 网关。 执行结果 在浏览器地址栏输入 http://{入口网关服务的IP地址}/productpage,可以看到 Bookinfo 应用的页面。按照如下步骤查看入口网关服务的 IP 地址: 登录容器服务控制台,选择路由与负载均衡 > 服务。 在服务(Service)页面,在集群和命名空间下拉列表中分别选择部署入口网关的集群和 istio-system。 查看名为 istio-ingressgateway 服务所对应的外部端点信息,即是入口网关服务的 IP 地址。 也可以通过 kubectl 客户端查询入口网关服务的 IP 地址。在对应部署入口网关的集群的环境下,执行以下命令查询: kubectl get service istio-ingressgateway -n istio-system -o jsonpath="{.status.loadBalancer.ingress[*].ip}" 由于 Reviews 微服务有 3 个版本,因此刷新页面可以看到 3 种不同的显示内容: v1 版本不会调用 Ratings 服务。 v2 版本会调用 Ratings 服务,并使用 1 到 5 个黑色星形图标来显示评分信息。 v3 版本会调用 Ratings 服务,并使用 1 到 5 个红色星形图标来显示评分信息。 当然,可以继续定义目标规则或者修改虚拟服务,使用Istio的其他更多功能。
前面的系列文章中已经提到Istio社区网站提供了一个适合于初学者的Bookinfo示例,通过这个示例可以很快了解它的一些基础概念和能力。同样地,在阿里云服务网格ASM产品中也提供了一个如何部署该Bookinfo示例的快速入门。本文介绍如何将一个应用示例部署到服务网格ASM 实例中的数据面集群中。 阿里云服务网格ASM公测来袭系列之一:快速了解什么是ASM 阿里云服务网格ASM公测来袭系列之二:快速创建一个服务网格ASM实例 阿里云服务网格ASM公测来袭系列之三:添加一个ACK集群到服务网格ASM实例 阿里云服务网格ASM公测来袭系列之四:添加入口网关 阿里云服务网格ASM公测来袭系列之五:部署应用到ASM的数据面集群中 阿里云服务网格ASM公测来袭系列之六:在ASM中为应用定义路由规则 欢迎扫码入群进一步交流: 前提条件 已创建至少一个 ASM 实例,并已添加至少一个 ACK 集群到该实例中。 已通过 Kubernetes 命令行客户端 kubectl 连接到 ASM 实例中新添加的 ACK 集群,详情参见通过 kubectl 连接 Kubernetes 集群。 本文中的应用示例需要对外暴露访问,因此需要提前为集群添加入口网关。 背景信息 本文所使用应用示例是一个名为 Bookinfo 的书评应用。微服务架构如下图所示: 该应用由以下 4 个微服务构成: Productpage:该微服务会调用 Details 和 Reviews 两个微服务,用来生成页面。 Details:该微服务包含了书籍的信息。 Reviews:该微服务包含了书籍相关的评论,同时会调用 Ratings 微服务。 Ratings:该微服务包含了由书籍评价组成的评级信息。 Reviews 微服务有 3 个版本: v1 版本不会调用 Ratings 服务。 v2 版本会调用 Ratings 服务,并使用 1 到 5 个黑色星形图标来显示评分信息。 v3 版本会调用 Ratings 服务,并使用 1 到 5 个红色星形图标来显示评分信息。 部署应用 登录容器服务控制台,在左侧导航栏中选择集群 > 命名空间。 在命名空间页面,从集群下拉列表中选择对应的集群。 在default命名空间的操作列,单击编辑。 在编辑命名空间对话框中,为该命名空间添加标签,单击添加。在变量名称文本框中输入 istio-injection,在变量值文本框中输入enabled。 或者,也可以通过 kubectl 执行以下命令为命名空间添加标签: kubectl label namespace default istio-injection=enabled 从 Github 的 Istio 项目库中下载 Bookinfo 的 YAML 文件。 通过 kubectl 执行以下命令,将 Bookinfo 应用部署到 ASM 实例的集群中。 kubectl apply -f bookinfo.yaml 执行结果 查看 Bookinfo 应用的部署情况。 登录容器服务控制台,在左侧导航栏中选择应用 > 容器组。 在容器组(Pod)页面,从集群下拉列表中选择对应的集群,从命名空间下拉列表中选择default。在容器组(Pod)页面可以查看到 Bookinfo 应用部署所对应的 Pod 信息。 如需查看详细信息,单击对应 POD 操作列的详情。
如果部署的应用需要对公网提供访问,需要部署一个入口网关到集群中。本文介绍如何为 ASM 实例中的 ACK 集群添加入口网关。 阿里云服务网格ASM公测来袭系列之一:快速了解什么是ASM 阿里云服务网格ASM公测来袭系列之二:快速创建一个服务网格ASM实例 阿里云服务网格ASM公测来袭系列之三:添加一个ACK集群到服务网格ASM实例 阿里云服务网格ASM公测来袭系列之四:添加入口网关 阿里云服务网格ASM公测来袭系列之五:部署应用到ASM的数据面集群中 阿里云服务网格ASM公测来袭系列之六:在ASM中为应用定义路由规则 欢迎扫码入群进一步交流: 背景信息 入口网关(Ingress Gateway)为 Kubernetes 集群提供了七层网关功能,对外提供一个统一的七层服务入口,根据 HTTP 请求的内容将来自同一个 TCP 端口的请求分发到不同的 Kubernetes 服务。 操作步骤 在数据平面区域,单击部署入口网关。 在部署入口网关页面,为集群添加入口网关。 从部署集群列表中选择要部署入口网关的集群。 指定负载均衡的类型,公网访问或内网访问。 选择已有或者新建负载均衡。 使用已有负载均衡:从已有负载均衡列表中选择。 新建负载均衡:单击新建负载均衡,从下拉列表中选择所需的负载均衡规格。 说明: 建议您为每个 Kubernetes 服务分配一个 SLB。如果多个 Kubernetes 服务复用同一个 SLB,存在以下风险和限制: 使用已有的 SLB 会强制覆盖已有监听,可能会导致您的应用不可访问。 Kubernetes 通过 Service 创建的 SLB 不能复用,只能复用您手动在控制台(或调用 OpenAPI)创建的 SLB。 复用同一个 SLB 的多个 Service 不能有相同的前端监听端口,否则会造成端口冲突。 复用 SLB 时,监听的名字以及虚拟服务器组的名字被 Kubernetes 作为唯一标识符。请勿修改监听和虚拟服务器组的名字。 不支持跨集群复用 SLB。 配置端口映射。单击添加端口。在新增端口行中,输入服务端口和容器端口。 说明: 建议容器端口与服务端口一致,并在 Istio 网关资源定义中启用了该端口。控制台默认提供了 4 个 Istio 常用的端口,但并不意味着必须从中选择,您可以根据需要自行添加或删除端口。 单击确定。 执行结果 添加入口网关之后,可登录容器服务控制台查看新添加的入口网关的服务信息。 登录容器服务控制台,在左侧导航栏中选择路由与负载均衡 > 服务。 在服务(Service)页面,从集群下拉列表中选择对应的集群,从命名空间下拉列表中选择istio-system。如需查看详细信息,单击操作列的详情。 如果要查看新添加的入口网关的 Pod 信息,在登录容器服务控制台之后,在左侧导航栏中选择应用 > 容器组。 在容器组(Pod)页面,从集群下拉列表中选择对应的集群,从命名空间下拉列表中选择istio-system。如需查看详细信息,单击操作列的详情。 在完成创建服务网格实例并添加了一个ACK集群到该实例之后,接下来就可以在这个服务网格中部署应用来使用网格的功能。
部署在服务网格中的应用实际上运行于集群之上,因此需要先给服务网格 ASM 实例的数据面添加一个 ACK Kubernetes 集群。 阿里云服务网格ASM公测来袭系列之一:快速了解什么是ASM 阿里云服务网格ASM公测来袭系列之二:快速创建一个服务网格ASM实例 阿里云服务网格ASM公测来袭系列之三:添加一个ACK集群到服务网格ASM实例 阿里云服务网格ASM公测来袭系列之四:添加入口网关 阿里云服务网格ASM公测来袭系列之五:部署应用到ASM的数据面集群中 阿里云服务网格ASM公测来袭系列之六:在ASM中为应用定义路由规则 欢迎扫码入群进一步交流: 前提条件 已创建至少一个服务网格ASM 实例。如果没有创建,请参见创建 快速创建一个服务网格ASM 实例。 已创建至少一个 ACK 集群。如果没有创建,请参见创建 Kubernetes 集群和创建Kubernetes 托管版集群。 待添加的 ACK 集群已开启公网 API Server,或者集群与 ASM 实例位于同一 VPC 以方便快速入门。 操作步骤 登录ASM 控制台。 在左侧导航栏中,选择网格实例,在右侧打开的页面中,选择要配置的 ASM 实例。 在数据平面区域,单击添加集群。 在添加集群页面,勾选需要添加的集群,然后单击确定。 说明: 如果应用服务运行于单集群或者同一 VPC 下的多集群时,建议先勾选与网格处于同一VPC的集群,筛选出与该网格处于同一 VPC 的集群。 请确保添加集群中运行的代理容器能访问 ASM 实例暴露的 Istio Pilot 地址。即:如果该 ASM 实例没有开放 Istio Pilot 公网地址,请确保能通过 VPC 进行访问。 添加集群检查如果添加的集群中已经存在istio-system命名空间,添加之前请确保已删除该命名空间,以避免可能存在的冲突问题。 打开容器服务Kubernetes控制台,进入该集群的命名空间列表,找到istio-system命名空间,并点击右侧的删除。 删除命名空间过程中如果遇到问题,请参考 如何删除处于终止状态的命名空间。 执行结果 添加集群之后,ASM 实例的状态变为更新网格中集群。数秒之后(时长与添加的集群数量有关),单击页面右上方的刷新,网格状态会变为运行中。在数据平面区域,可以查看已添加集群的信息。 接下来,将会讲述如何部署一个入口网关到ACK Kubernetes集群中,以便于部署的应用可以对公网提供访问。
Istio社区网站提供了一个适合于初学者的Bookinfo示例,通过这个示例可以很快了解它的一些基础概念和能力。同样地,在阿里云服务网格ASM产品中也提供了一个如何部署该Bookinfo示例的快速入门。 阿里云服务网格ASM公测来袭系列之一:快速了解什么是ASM 阿里云服务网格ASM公测来袭系列之二:快速创建一个服务网格ASM实例 阿里云服务网格ASM公测来袭系列之三:添加一个ACK集群到服务网格ASM实例 阿里云服务网格ASM公测来袭系列之四:添加入口网关 阿里云服务网格ASM公测来袭系列之五:部署应用到ASM的数据面集群中 阿里云服务网格ASM公测来袭系列之六:在ASM中为应用定义路由规则 欢迎扫码入群进一步交流: 操作步骤 该入门教程将会按照如下几个步骤: 创建一个服务网格ASM 实例(控制面); 添加一个ACK Kubernetes集群(数据面)到服务网格ASM 实例(控制面)中; 为服务网格 ASM 实例(数据面)中的集群部署入口网关; 将应用部署到上述服务网格 ASM 实例中的ACK Kubernetes集群(数据面)中; 为服务网格ASM 实例(控制面)定义虚拟服务和 Istio 网关; 创建一个服务网格ASM 实例(控制面) 在使用服务网格 ASM 之前,您需要创建一个 ASM 实例。 前提条件 已开通以下服务: 服务网格 ASM 容器服务Kubernetes 弹性伸缩(ESS)服务 访问控制(RAM)服务 链路追踪服务(如需启用链路追踪功能) 背景信息 创建服务网格的过程中,根据不同的配置,ASM 可能会进行如下操作: 创建安全组,该安全组允许 VPC 入方向全部 ICMP 端口的访问 创建 VPC 路由规则 创建 EIP 创建 RAM 角色及相应策略,该角色拥有SLB 的全部权限,云监控的全部权限,VPC 的全部权限,日志服务的全部权限。服务网格会根据用户部署的配置相应的动态创建 SLB、VPC 路由规则等 创建专有网 SLB,暴露 6443 端口 创建专有网 SLB,暴露 15011 端口 在使用服务网格的过程中,ASM 会收集被托管管控组件的日志信息用于稳定性保障 操作步骤 登录ASM 控制台。 在左侧导航栏中选择网格实例,然后在右侧打开的页面中,单击创建新网格。 在创建新网格页面,填写网格的名称、选择相应的地域、专有网络 VPC 及交换机。 说明: 您可以在已有 VPC 列表和交换机列表中选择所需的 VPC 和交换机。如果没有您需要的 VPC 或交换机,可以通过单击创建专有网络或创建交换机进行创建,请参见创建专有网络或创建交换机。 设置是否开放使用公网地址暴露 API Server。ASM 实例的运行基于 Kubernetes 运行时,可以通过 API Server 定义执行各种网格资源,如虚拟服务、目标规则或者 Istio 网关等。 如果选择开放,会创建一个 EIP,并挂载到私网 SLB 上。API Server 的 6443 端口会暴露出来,您可以在公网通过 kubeconfig 来连接和操作集群,从而定义网格资源。 如果选择不开放,则不会创建 EIP,您只能在 VPC 下通过 kubeconfig 来连接和操作集群,从而定义网格资源。 设置是否开放使用公网地址暴露 Istio Pilot。 如果选择开放,会创建一个 EIP,并挂载到私网 SLB 上。Istio Pilot 的 15011 端口会暴露出来,数据平面侧集群中部署的 Envoy 代理通过该公网地址连接到 Istio Pilot。 如果选择不开放,则不会创建 EIP,数据平面侧只能添加与该 VPC 互连的集群,包括同一 VPC 下的集群或者通过云企业网连通的跨 VPC 集群。 说明 默认不开放公网地址暴露 Istio Pilot,优先通过 VPC 连通数据平面与控制平面。 可观测性:设置是否启用链路追踪。ASM 集成了阿里云链路追踪服务,为分布式应用的开发者提供了完整的调用链路还原、调用请求量统计、链路拓扑、应用依赖分析等能力,可以帮助开发者快速分析和诊断分布式应用架构下的性能瓶颈,提升开发诊断效率。 说明: 启用该配置之前,您需要登录链路追踪管理控制台开通链路追踪服务。 流量管理:设置是否启用服务就近访问。服务网格ASM通过Envoy代理为应用服务提供了全局负载均衡能力,您可以在多个跨地域的ACK集群中部署运行应用服务的实例。ASM将这些应用服务的运行状况、路由和后端信息提供给Envoy代理,使其能够以最佳方式将流量路由至某个服务位于多个地域的应用实例。ASM会根据发送请求的Envoy代理位置,针对目标服务的工作负载实例,进行优先级排序。开启该项功能之后,当所有实例都正常时,请求将保留在同一位置,即保持服务就近访问。 策略控制:设置是否启用OPA插件。服务网格ASM集成了开放策略代理OPA,可用于为您的应用程序实现细粒度的访问控制。例如,可以使用OPA 跨微服务实现授权等。启用后,如同Istio Envoy代理容器一样,OPA代理容器也会随之被注入到业务Pod中。然后,在ASM中就可以使用OPA定义访问控制策略,为分布式应用的开发者提供了开箱可用的能力,可以帮助开发者快速定义使用策略,提升开发效率。 服务协议了解和接受服务协议,并已阅读和同意阿里云服务网格服务条款和免责声明,然后选中该选项。 单击确定,开始实例的创建。 说明 一个 ASM 实例的创建时间一般约为 2 到 3 分钟。
阿里云服务网格(Alibaba Cloud Service Mesh,简称 ASM)正式发布公测版本。服务网格(简称ASM)是业内首个全托管Istio兼容的服务网格平台,支持运行于多种类型的计算基础设施ACK(阿里云托管式或专有式标准Kubernetes集群)、ASK(阿里云Serverless Kubernetes集群)、ECS(阿里云服务器)、ECI(阿里云弹性容器实例)等之上的服务应用流量统一管理 。该产品提供了一个全托管式的服务网格平台,兼容于社区 Istio 开源服务网格,用于简化服务的治理,包括服务调用之间的流量路由与拆分管理、服务间通信的认证安全以及网格可观测性能力,从而极大地减轻开发与运维的工作负担。 阿里云服务网格ASM公测来袭系列之一:快速了解什么是ASM 阿里云服务网格ASM公测来袭系列之二:快速创建一个服务网格ASM实例 阿里云服务网格ASM公测来袭系列之三:添加一个ACK集群到服务网格ASM实例 阿里云服务网格ASM公测来袭系列之四:添加入口网关 阿里云服务网格ASM公测来袭系列之五:部署应用到ASM的数据面集群中 阿里云服务网格ASM公测来袭系列之六:在ASM中为应用定义路由规则 欢迎扫码入群进一步交流: 【快速链接】产品页面: https://www.aliyun.com/product/servicemesh产品文档:https://help.aliyun.com/product/147365.html 【产品架构】在 ASM 中,Istio 控制平面的组件全部托管,降低用户使用的复杂度,用户只需要专注于业务应用的开发部署。同时,保持与Istio社区的兼容,支持声明式的方式定义灵活的路由规则,支持网格内服务之间的统一流量管理。 一个托管了控制平面的 ASM 实例可以支持来自多个 Kubernetes 集群的应用服务或者运行于 ECI Pod 上的应用服务。此外,也可以把一些非 Kubernetes 服务(例如运行于虚拟机或物理裸机中的服务)集成到同一个服务网格中。 ASM 的产品架构如下图所示。 【功能特性】ASM 定位于混合云、多云、多集群、非容器应用迁移等核心场景中,构建托管式统一的服务网格能力,能够为阿里云用户提供以下功能: 一致的管理方式以一致的方式来管理运行于 ACK 托管 Kubernetes 集群、专有 Kubernetes 集群、Serverless Kubernetes 集群、混合云或多云场景下的接入集群上的应用服务,从而提供一致的可观测性和流量控制 统一的流量管理支持容器或者虚拟机混合环境下统一的流量管理 控制平面核心组件托管化托管控制平面的核心组件,最大限度地降低用户资源开销和运维成 可扩展的插件式的数据平面这些诸如监控、跟踪、限流等数据面能力都是采用基于Envoy的插件化的统一集成方式;此外采用WebAssembly技术使得数据面的扩展更加灵活。 【欢迎使用】我们提供了一个快速入门的教程帮助用户去了解学习如何使用该产品,欢迎体验:快速入门 产品页面: https://www.aliyun.com/product/servicemesh产品文档:https://help.aliyun.com/product/147365.html
阿里云服务网格(Alibaba Cloud Service Mesh,简称 ASM)正式发布公测版本。该产品提供了一个全托管式的服务网格平台,兼容于社区 Istio 开源服务网格,用于简化服务的治理,包括服务调用之间的流量路由与拆分管理、服务间通信的认证安全以及网格可观测性能力,从而极大地减轻开发与运维的工作负担。 【快速链接】产品页面: https://www.aliyun.com/product/servicemesh产品文档:https://help.aliyun.com/product/147365.html 【产品架构】在 ASM 中,Istio 控制平面的组件全部托管,降低用户使用的复杂度,用户只需要专注于业务应用的开发部署。同时,保持与Istio社区的兼容,支持声明式的方式定义灵活的路由规则,支持网格内服务之间的统一流量管理。 一个托管了控制平面的 ASM 实例可以支持来自多个 Kubernetes 集群的应用服务或者运行于 ECI Pod 上的应用服务。此外,也可以把一些非 Kubernetes 服务(例如运行于虚拟机或物理裸机中的服务)集成到同一个服务网格中。 ASM 的产品架构如下图所示。 【功能特性】ASM 定位于混合云、多云、多集群、非容器应用迁移等核心场景中,构建托管式统一的服务网格能力,能够为阿里云用户提供以下功能: 一致的管理方式以一致的方式来管理运行于 ACK 托管 Kubernetes 集群、专有 Kubernetes 集群、ServerlessKubernetes 集群、混合云或多云场景下的接入集群上的应用服务,从而提供一致的可观测性和流量控制 统一的流量管理支持容器或者虚拟机混合环境下统一的流量管理 控制平面核心组件托管化托管控制平面的核心组件,最大限度地降低用户资源开销和运维成 【欢迎使用】我们提供了一个快速入门的教程帮助用户去了解学习如何使用该产品,欢迎体验:快速入门 产品页面: https://www.aliyun.com/product/servicemesh产品文档:https://help.aliyun.com/product/147365.html
目前阿里云云原生产品家族已经支持多集群管理功能,允许使用阿里云容器服务Kubernetes(简称ACK)控制台或kubectl命令接入、统一纳管其他公有云、客户IDC自建K8s集群,集中管理部署K8s工作负载;并可以针对工作负载流量统一管理,支持服务就近访问、故障转移能力。本文重点介绍如何使用ACK控制台来接入一个外部Kubernetes集群,无论这个集群是否提供公网访问能力,或者该集群是来自其他公有云提供商,亦或是用户IDC自定义集群。 前提条件 您需要开通容器服务、弹性伸缩(ESS)服务和访问控制(RAM)服务。登录 容器服务管理控制台 、 RAM 管理控制台 和 弹性伸缩控制台 开通相应的服务。 修改Kubernetes集群仍然必须在原有的集群中进行完成,包括添加与删除节点、升级Kubernetes集群版本以及更改Kubernetes组件参数等。 该功能目前只针对白名单客户开放,扫描以下二维码开启邀测: Demo示例 为了帮助您更好地理解,以下是我们制作的一个简短的视频Demo,演示如何在ACK控制台上统一纳管一个Google GKE K8s集群。 下载创建自有Kubernetes集群用于接入外部集群 按照以下步骤可以在ACK控制台中创建自有Kubernetes集群用于接入外部集群,具体如下: 登录 容器服务管理控制台, 在 Kubernetes 菜单下,单击左侧导航栏的集群 > 集群,进入集群列表页面。 单击页面右上角的创建 Kubernetes 集群,在弹出的选择集群模板中,选择导入已有Kubernetes集群,单击创建。 默认进入导入已有 Kubernetes 集群配置页面,如下所示。 填写集群的名称,例如test-external-cluster1。集群名称应包含1-63个字符,可包含数字、汉字、英文字符或连字符(-)。 选择集群所在的地域和可用区。 设置集群的网络。Kubernetes 集群仅支持专有网络。您可以在已有 VPC 列表中选择所需的 VPC 和交换机。需要绑定EIP,用于建立集群链接。 单击创建集群,启动创建过程。 接入外部Kubernetes集群 按照以下步骤可以在ACK控制台中接入外部Kubernetes集群,具体如下: 在新集群test-external-cluster1右侧单击管理,进入基本信息页面。在页面下方,可以看到集群导入代理配置: 单击复制,将其中的yaml文件内容拷贝到agent.yaml文件中,并在目标集群中执行命令:kubectl apply -f agent.yaml。 在目标集群中执行kubectl get all -n ack-system命令,查看代理运行状况。 NAME READY STATUS RESTARTS AGE pod/ack-cluster-agent-655b75c987-dwp6b 1/1 Running 0 9s NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE deployment.apps/ack-cluster-agent 1 1 1 1 26m NAME DESIRED CURRENT READY AGE replicaset.apps/ack-cluster-agent-655b75c987 1 1 1 26m 可以在容器服务管理控制台的 Kubernetes 集群列表页面,看到新集群的状态为运行中。在集群列表页面中,找到新集群test-external-cluster1,单击操作列中的管理,查看新集群的基本信息和连接信息。 使用ACK控制台或者kubectl管理接入的集群 接入成功之后,可以通过 kubectl 连接 Kubernetes 集群,执行kubectl get node命令,查看新集群test-external-cluster1的节点信息。此时,您可以使用该kubeconfig连接远程的被接入集群,进行应用负载的部署。 同样地,可以在该接入的集群中通过使用Helm来发布管理应用: 总结 目前阿里云云原生产品家族已经支持多集群管理功能,允许使用阿里云容器服务Kubernetes(简称ACK)控制台或kubectl命令导入、统一纳管其他公有云、客户IDC自建K8s集群,集中管理部署K8s工作负载;并可以针对工作负载流量统一管理,支持服务就近访问、故障转移能力。本文重点介绍了如何使用ACK控制台来接入一个外部Kubernetes集群,后续讲继续介绍如何在接入的多个集群中统一部署管理应用以及如何实现工作负载、流量的统一管理。
阿里云容器服务Kubernetes(简称ACK)支持一键部署Istio,可以参考文档在ACK上部署使用Isito。Istio on ACK提供了丰富的监控能力,为网格中的服务收集遥测数据,其中Mixer是负责提供策略控制和遥测收集的Istio组件。使用Prometheus进行监控是Istio提供的监控能力之一。 告警能力在Prometheus的架构中被划分成两个独立的部分:Prometheus负责产生告警,而Alertmanager负责告警产生后的后续处理。如下所示,通过在Prometheus中定义告警规则,Prometheus会周期性的对告警规则进行计算,如果满足告警触发条件就会向Alertmanager发送告警信息。 Alertmanager作为一个独立的组件,负责接收并处理来自Prometheus Server(也可以是其它的客户端程序)的告警信息。Alertmanager可以对这些告警信息进行进一步的处理,比如当接收到大量重复告警时能够消除重复的告警信息,同时对告警信息进行分组并且路由到正确的通知方,Prometheus内置了对邮件、Slack等多种通知方式的支持,同时还支持与Webhook的集成,以支持更多定制化的场景。例如,完全可以通过Webhook与钉钉机器人进行集成,从而通过钉钉接收告警信息。同时AlertManager还提供了静默和告警抑制机制来对告警通知行为进行优化。 以下介绍如何扩展AlertManager集成钉钉,并通过AlertManager帮助实现Istio on ACK在可观测性监控方面的能力。 通过Webhook集成钉钉 单击钉钉群右上角群设置图标,进入群设置页面。单击群机器人,进入群机器人页面,选择需要添加的机器人。此处选择自定义机器人。 在机器人详情页面,单击添加,进入添加机器人页面。 填写完配置群机器人信息后,单击完成添加。 单击复制,复制webhook地址。 部署AlertManager并对接钉钉 登录容器服务管理控制台。 在Kubernetes菜单下,单击左侧导航栏中的应用 > 无状态,进入 无状态(Deployment)页面。 选择目标集群,命名空间选为istio-system,单击右上角使用模板创建。 根据以下信息配置模板,完成后单击创建。 配置 说明 集群 选择目标集群。 命名空间 选择资源对象所属的命名空间,默认是 default。此处选择istio-system。 示例模板 此处选择自定义。 模板 填写以下自定义内容。 自定义YAML内容如下: apiVersion: v1 kind: Service metadata: name: dingtalkservice labels: app: dingtalkservice service: dingtalkservice spec: ports: - port: 8060 name: http selector: app: dingtalkservice --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: dingtalkservice labels: app: dingtalkservice version: v1 spec: replicas: 1 template: metadata: labels: app: dingtalkservice version: v1 spec: containers: - name: prometheus-webhook-dingtalk image: timonwong/prometheus-webhook-dingtalk imagePullPolicy: IfNotPresent args: - --ding.profile=webhook1={替换为上述步骤中复制的webhook地址} ports: - containerPort: 8060 --- kind: ConfigMap apiVersion: v1 metadata: name: alertmanager data: config.yml: |- global: resolve_timeout: 5m templates: - '/etc/alertmanager-templates/*.tmpl' route: group_by: ['alertname', 'cluster', 'service'] group_wait: 30s group_interval: 5m repeat_interval: 1m receiver: webhook_alert routes: - match: severity: info receiver: webhook_alert - match: severity: warning receiver: webhook_alert receivers: - name: webhook_alert webhook_configs: - url: 'http://dingtalkservice:8060/dingtalk/webhook1/send' send_resolved: false --- apiVersion: v1 kind: Service metadata: annotations: prometheus.io/scrape: 'true' prometheus.io/path: '/metrics' labels: name: alertmanager name: alertmanager spec: selector: app: alertmanager type: ClusterIP ports: - name: alertmanager protocol: TCP port: 9093 targetPort: 9093 --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: alertmanager spec: replicas: 1 selector: matchLabels: app: alertmanager template: metadata: name: alertmanager labels: app: alertmanager spec: containers: - name: alertmanager image: prom/alertmanager:v0.15.0 args: - '--config.file=/etc/alertmanager/config.yml' - '--storage.path=/alertmanager' ports: - name: alertmanager containerPort: 9093 volumeMounts: - name: config-volume mountPath: /etc/alertmanager - name: alertmanager mountPath: /alertmanager serviceAccountName: prometheus volumes: - name: config-volume configMap: name: alertmanager - name: alertmanager emptyDir: {} 创建成功之后,单击左侧导航栏中的应用 > 容器组,选择相应的集群和命名空间istio-system, 可以看到如下类似的运行中的alertmanager和dingtalkservice容器组。 创建Prometheus告警规则配置项 登录 容器服务管理控制台。在Kubernetes菜单下,单击左侧导航栏中的应用配置 > 配置项,选择相应的集群与命名空间istio-system,点击右上角的创建按钮,进入创建配置项页面。 输入配置项名称:prom-rules1。 添加配置项,名称为rule1.yaml,值为如下内容: groups: - name: fake rules: - alert: rules-alert expr: | histogram_quantile(0.99, sum by(source_app, source_version, destination_service, destination_version, le) (irate(istio_request_duration_seconds_bucket[1m])) ) > 3 for: 1m labels: alertname: "request-duration-3" annotations: summary: "Request duration gt 3" from: "{{ $labels.source_app }}:{{ $labels.source_version }}" to: "{{ $labels.destination_service }}:{{ $labels.destination_version }}" 该规则描述过去1分钟内99%请求时延超过3s时会发出告警。 点击确定按钮。 集成AlertManager到Istio中 阿里云容器服务Kubernetes(简称ACK)支持一键部署Istio,可以参考文档在ACK上部署Isito。默认部署中的Prometheus服务没有对接AlertManager,需要按照如下步骤进行配置。 登录容器服务管理控制台。 在 Kubernetes 菜单下,单击左侧导航栏的应用 > 发布,进入发布页面。 单击Helm,选择所需的集群,选择待更新的Istio,单击操作列的更新。 在弹出的对话框中,对Istio的Prometheus参数进行修改: | 配置 | 说明 | enabled true或者false,表示是否启用Prometheus 收集度量日志。默认情况下启用,即值为true。 replicaCount prometheus容器组的副本数,默认值为1。 persist true或者false,表示是否启用持久化存储。设置为true时,必须指定TSDB实例地址。 tsdbEndpoint TSDB实例地址,启用持久化存储时必须指定。 retention 默认的数据保留时间,8760h0m0s即为24*365小时,即1年 scrapeInterval 全局默认抓取时间间隔,默认为15s prometheusRulesConfigMap 告警规则配置项的名称 alerting 配置对接的AlertManager服务,配置如下代码所示 alerting: alertmanagers: - static_configs: - targets: ["alertmanager:9093"] 修改完毕之后,单击更新。 告警规则触发验证 在Prometheus控制台上,点击Alerts页签,可以看到如下类似内容。 同时,相应的钉钉群也会收到类似的告警信息,如下所示。 总结 在阿里云Kubernetes容器服务基础之上,快速搭建一套用于连接、管理以及安全化微服务的开放平台Istio,为应用引入和配置多个相关服务。使用Prometheus进行监控是Istio提供的监控能力之一,通过扩展AlertManager集成钉钉助力Istio on ACK可观测性监控能力。
阿里云容器服务Kubernetes(简称ACK)支持一键部署Istio,可以参考文档在ACK上部署使用Isito。Istio on ACK提供了丰富的监控能力,为网格中的服务收集遥测数据,其中Mixer是负责提供策略控制和遥测收集的Istio组件。使用Prometheus进行监控是Istio提供的监控能力之一。 告警能力在Prometheus的架构中被划分成两个独立的部分:Prometheus负责产生告警,而Alertmanager负责告警产生后的后续处理。如下所示,通过在Prometheus中定义告警规则,Prometheus会周期性的对告警规则进行计算,如果满足告警触发条件就会向Alertmanager发送告警信息。 Alertmanager作为一个独立的组件,负责接收并处理来自Prometheus Server(也可以是其它的客户端程序)的告警信息。Alertmanager可以对这些告警信息进行进一步的处理,比如当接收到大量重复告警时能够消除重复的告警信息,同时对告警信息进行分组并且路由到正确的通知方,Prometheus内置了对邮件、Slack等多种通知方式的支持,同时还支持与Webhook的集成,以支持更多定制化的场景。例如,完全可以通过Webhook与钉钉机器人进行集成,从而通过钉钉接收告警信息。同时AlertManager还提供了静默和告警抑制机制来对告警通知行为进行优化。 以下介绍如何扩展AlertManager集成钉钉,并通过AlertManager帮助实现Istio on ACK在可观测性监控方面的能力。 通过Webhook集成钉钉 单击钉钉群右上角群设置图标,进入群设置页面。单击群机器人,进入群机器人页面,选择需要添加的机器人。此处选择自定义机器人。 在机器人详情页面,单击添加,进入添加机器人页面。 填写完配置群机器人信息后,单击完成添加。 单击复制,复制webhook地址。 部署AlertManager并对接钉钉 登录容器服务管理控制台。 在Kubernetes菜单下,单击左侧导航栏中的应用 > 无状态,进入 无状态(Deployment)页面。 选择目标集群,命名空间选为istio-system,单击右上角使用模板创建。 根据以下信息配置模板,完成后单击创建。 配置 说明 集群 选择目标集群。 命名空间 选择资源对象所属的命名空间,默认是 default。此处选择istio-system。 示例模板 此处选择自定义。 模板 填写以下自定义内容。 自定义YAML内容如下: apiVersion: v1 kind: Service metadata: name: dingtalkservice labels: app: dingtalkservice service: dingtalkservice spec: ports: - port: 8060 name: http selector: app: dingtalkservice --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: dingtalkservice labels: app: dingtalkservice version: v1 spec: replicas: 1 template: metadata: labels: app: dingtalkservice version: v1 spec: containers: - name: prometheus-webhook-dingtalk image: timonwong/prometheus-webhook-dingtalk imagePullPolicy: IfNotPresent args: - --ding.profile=webhook1={替换为上述步骤中复制的webhook地址} ports: - containerPort: 8060 --- kind: ConfigMap apiVersion: v1 metadata: name: alertmanager data: config.yml: |- global: resolve_timeout: 5m templates: - '/etc/alertmanager-templates/*.tmpl' route: group_by: ['alertname', 'cluster', 'service'] group_wait: 30s group_interval: 5m repeat_interval: 1m receiver: webhook_alert routes: - match: severity: info receiver: webhook_alert - match: severity: warning receiver: webhook_alert receivers: - name: webhook_alert webhook_configs: - url: 'http://dingtalkservice:8060/dingtalk/webhook1/send' send_resolved: false --- apiVersion: v1 kind: Service metadata: annotations: prometheus.io/scrape: 'true' prometheus.io/path: '/metrics' labels: name: alertmanager name: alertmanager spec: selector: app: alertmanager type: ClusterIP ports: - name: alertmanager protocol: TCP port: 9093 targetPort: 9093 --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: alertmanager spec: replicas: 1 selector: matchLabels: app: alertmanager template: metadata: name: alertmanager labels: app: alertmanager spec: containers: - name: alertmanager image: prom/alertmanager:v0.15.0 args: - '--config.file=/etc/alertmanager/config.yml' - '--storage.path=/alertmanager' ports: - name: alertmanager containerPort: 9093 volumeMounts: - name: config-volume mountPath: /etc/alertmanager - name: alertmanager mountPath: /alertmanager serviceAccountName: prometheus volumes: - name: config-volume configMap: name: alertmanager - name: alertmanager emptyDir: {} 创建成功之后,单击左侧导航栏中的应用 > 容器组,选择相应的集群和命名空间istio-system, 可以看到如下类似的运行中的alertmanager和dingtalkservice容器组。 创建Prometheus告警规则配置项 登录 容器服务管理控制台。在Kubernetes菜单下,单击左侧导航栏中的应用配置 > 配置项,选择相应的集群与命名空间istio-system,点击右上角的创建按钮,进入创建配置项页面。 输入配置项名称:prom-rules1。 添加配置项,名称为rule1.yaml,值为如下内容: groups: - name: fake rules: - alert: rules-alert expr: | histogram_quantile(0.99, sum by(source_app, source_version, destination_service, destination_version, le) (irate(istio_request_duration_seconds_bucket[1m])) ) > 3 for: 1m labels: alertname: "request-duration-3" annotations: summary: "Request duration gt 3" from: "{{ $labels.source_app }}:{{ $labels.source_version }}" to: "{{ $labels.destination_service }}:{{ $labels.destination_version }}" 该规则描述过去1分钟内99%请求时延超过3s时会发出告警。 点击确定按钮。 集成AlertManager到Istio中 阿里云容器服务Kubernetes(简称ACK)支持一键部署Istio,可以参考文档在ACK上部署Isito。默认部署中的Prometheus服务没有对接AlertManager,需要按照如下步骤进行配置。 登录容器服务管理控制台。 在 Kubernetes 菜单下,单击左侧导航栏的应用 > 发布,进入发布页面。 单击Helm,选择所需的集群,选择待更新的Istio,单击操作列的更新。 在弹出的对话框中,对Istio的Prometheus参数进行修改: | 配置 | 说明 | enabled true或者false,表示是否启用Prometheus 收集度量日志。默认情况下启用,即值为true。 replicaCount prometheus容器组的副本数,默认值为1。 persist true或者false,表示是否启用持久化存储。设置为true时,必须指定TSDB实例地址。 tsdbEndpoint TSDB实例地址,启用持久化存储时必须指定。 retention 默认的数据保留时间,8760h0m0s即为24*365小时,即1年 scrapeInterval 全局默认抓取时间间隔,默认为15s prometheusRulesConfigMap 告警规则配置项的名称 alerting 配置对接的AlertManager服务,配置如下代码所示 alerting: alertmanagers: - static_configs: - targets: ["alertmanager:9093"] 修改完毕之后,单击更新。 告警规则触发验证 当你的应用服务调用满足触发条件时,在Prometheus控制台上,点击Alerts页签,可以看到如下类似内容。 同时,相应的钉钉群也会收到类似的告警信息,如下所示。 总结 在阿里云Kubernetes容器服务基础之上,快速搭建一套用于连接、管理以及安全化微服务的开放平台Istio,为应用引入和配置多个相关服务。使用Prometheus进行监控是Istio提供的监控能力之一,通过扩展AlertManager集成钉钉助力Istio on ACK可观测性监控能力。
阿里云容器服务Kubernetes(简称ACK)支持一键部署Istio,可以参考文档在ACK上部署使用Isito。Istio on ACK提供了丰富的监控能力,为网格中的服务收集遥测数据,其中Mixer是负责提供策略控制和遥测收集的Istio组件。使用Prometheus进行监控是Istio提供的监控能力之一。Prometheus是一个开源的监控和报警系统,Prometheus依赖少,功能齐全,广泛用于Kubernetes集群的监控系统中。Istio自0.8版本开始默认将Prometheus包含在内,Mixer支持对接到Prometheus监控设施的Adapter。用户可以通过查询service或pod看到Prometheus的运行状态和地址。也可以通过简洁明了的Prometheus的UI界面查看监测数据。 Prometheus的存储挑战及解决方案 Prometheus的本地存储设计可以减少其自身运维和管理的复杂度,能够满足大部分用户监控规模的需求,但是本地存储也意味着Prometheus无法持久化数据,无法存储大量历史数据,同时也无法灵活扩展。Prometheus本身没有尝试解决以上问题,而是通过定义一组remote storage adapter标准接口,让用户可以基于这组标准接口自主决定将Promthues中的监控样本数据存储至第三方的远端存储服务中,来解决本地存储带来的问题。 TSDB for Prometheus是一种高性能,低成本,稳定可靠的在线时序数据库服务,通过内置实现的Prometheus的remote storage adapter,天然原生支持作为Promtheus的第三方在线远端存储服务。 相较于其他第三方远端存储而言,TSDB for Prometheus具有集成程度高,同时支持读写等优势。 集成程度高目前Prometheus对接第三方存储系统的主流做法是实现一个独立于第三方存储系统的remote storage adapter, 这个adapter需要单独部署和运维,另外单独部署的remote storage adapter还存在单点故障和读写性能问题。 TSDB for Prometheus在服务端内置实现的Prometheus remote storage adapter,只需在Prometheus的配置文件prometheus.yaml中修改下远程读写配置,即可原生支持Prometheus直接读写TSDB,集成程度高,无需额外单独部署adapter,极大地降低了adapter的单点故障风险和运维成本。 同时支持读写当前一些第三方远端存储仅支持Prometheus将监控样本数据写入,而不支持读取。比如对于OpenTSDB、Graphite和Elasticsearch等第三方存储,Prometheus社区提供的adapter只支持写入模式,不支持读取模式。而TSDB for Prometheus同时支持读写。如要了解更多第三方存储系统对于Prometheus读写的支持程度,请参考Remote Endpoints and Storage。 TSDB for Prometheus 时序时空数据库(Time Series & Spatial Temporal Database,简称 TSDB)是一种高性能、低成本、稳定可靠的在线时序时空数据库服务,提供高效读写、高压缩比存储、时序数据插值及聚合计算等服务。TSDB 具备秒级写入百万级时序数据的性能,提供高压缩比低成本存储、预降采样、插值、多维聚合计算、可视化查询结果等功能。 TSDB for Prometheus是阿里云时序时空数据库TSDB为Prometheus提供的一种高性能、低成本、稳定可靠的在线远端存储服务,具备以下能力: 提供高效读写、高压缩比存储的能力,可无缝被Prometheus集成,在协议上原生支持Prometheus远端存储对接至TSDB。 解决了以往需要为Prometheus额外开发remote storage adapter的问题,极大的降低了Prometheus远端存储对接TSDB的成本。 TSDB for Prometheus最大程度的兼容Prometheus的PromQL查询语法,从而降低了用户的开发,迁移和维护成本。 解决Prometheus local stroge 无法存储大量历史数据的场景,且无法扩展的问题。 通过内置实现的Prometheus的remote storage adapter,天然原生支持作为Promtheus的第三方在线远端存储服务。 相较于其他第三方远端存储而言,TSDB for Prometheus具有集成程度高,同时支持读写等优势。 TSDB for Prometheus的使用要求具体可以参见使用要求 阿里云提供的不同规格的TSDB实例,设置了不同的最大写入TPS,避免过大TPS导致TSDB示例不可用,保护TSDB实例正常运行。当写入TPS超过TSDB实例允许的最大TPS时,将触发TSDB实例限流保护规则,会造成写入失败异常。因此需要根据TSDB实例规格来调整Prometheus的remote_write配置,从而实现平稳可靠的将Prometheus采集到的指标写入TSDB中。 关于Remote Write配置,除了参考Prometheus官方提供的Remote Write配置说明之外,还可以参考Prometheus对接阿里云TSDB时的写入配置最佳实践。 创建TSDB实例 创建一个TSDB实例非常简单,登录TSDB 控制台,参照创建实例文档即可创建。获取开通的TSDB实例的地址,可以参考TSDB官方文档快速入门 确认Prometheus所在机器能够正常访问TSDB实例。直接使用http访问TSDB实例的地址,如果能够得到包含”Welcome to use the TSDB”的字符串,表示Prometheus所在机器能够正常访问TSDB实例。 设TSDB实例的公网地址为: ts-xxxxxxxxxxxx.hitsdb.rds.aliyuncs.com:3242,在启用了Prometheus的Istio所在Kubernetes集群下,执行命令:kubectl exec -it -n istio-system $(kubectl get pods -n istio-system -l app=prometheus -o jsonpath='{.items[0].metadata.name}') sh 进入到Prometheus容器之后,执行命令wget -S ts-xxxxxxxxxxxx.hitsdb.rds.aliyuncs.com:3242,可以得到如下结果: Connecting to ts-bp1839ak5uf20igcw.hitsdb.rds.aliyuncs.com:3242 (101.37.143.44:3242) HTTP/1.1 200 OK Content-Type: Content-Type index.html 100% |*****************************************************************************************| 361 0:00:00 ETA 然后执行命令 cat index.html,可以得到如下结果: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv=content-type content="text/html;charset=utf-8"><title>TSDB</title> <style><!-- body{font-family:arial,sans-serif;margin-left:2em}A.l:link{color:#6f6f6f}A.u:link{color:green}.fwf{font-family:monospace;white-space:pre-wrap}//--></style><div>Welcome to use the TSDB!</div> 集成TSDB到Istio on ACK 第一次部署Istio 阿里云容器服务Kubernetes(简称ACK)支持一键部署Istio,可以参考文档在ACK上部署Isito。如果是第一次部署使用Isito,可以直接按照文档步骤进行部署。注意,勾选启用Prometheus 度量日志收集选项,默认情况下启用,并勾选持久化存储,提示需要提供可以访问的TSDB地址。此处填写在上述步骤中已经验证可以使用的TSDB实例的地址,例如ts-xxxxxxxxxxxx.hitsdb.rds.aliyuncs.com:3242。 然后单击部署 Istio,启动部署。部署成功之后,Prometheus的监控数据将持久化到对应的TSDB实例中。 更改已有Isito部署配置 如果之前已经部署过Istio,但没有启用Prometheus的持久化存储,则可以按照如下步骤进行配置。 登录容器服务管理控制台。 在 Kubernetes 菜单下,单击左侧导航栏的应用 > 发布,进入发布页面。 单击Helm,选择所需的集群,选择待更新的Istio,单击操作列的更新。 在弹出的对话框中,对Istio的Prometheus参数进行修改: 配置 说明 enabled true或者false,表示是否启用Prometheus 收集度量日志。默认情况下启用,即值为true。 replicaCount prometheus容器组的副本数,默认值为1。 persist true或者false,表示是否启用持久化存储。设置为true时,必须指定TSDB实例地址。 tsdbEndpoint TSDB实例地址,启用持久化存储时必须指定。 retention 默认的数据保留时间,8760h0m0s即为24*365小时,即1年 scrapeInterval 全局默认抓取时间间隔,默认为15s 修改完毕之后,单击更新。 查看Prometheus配置 登录容器服务管理控制台,单击左侧导航栏中的应用配置下的配置项,进入配置项列表页面。点击名称为prometheus的配置项,进入配置项明细页面,如下图所示,可以看到对应的TSDB实例的地址: TSDB写入和读取验证 TSDB写入验证 可以简单的通过TSDB实例的管理控制台界面的“实例监控”界面,观察写入的TPS是否有所变化来验证。一般刚开始没有任何数据写入时,写入的TPS为0;若TPS开始从0变为正数时,则说明Prometheus已经将数据上报到了TSDB中,如下图所示。 读取验证 在启用了Prometheus的Istio所在Kubernetes集群下,执行命令:kubectl port-forward -n istio-system svc/prometheus 9090:9090 打开浏览器,输入地址http://localhost:9090,进入Prometheus控制台页面。 Istio提供了一系列的默认监控指标,以Prometheus采集到Istio的一个监控指标istio_request_bytes_count为例进行查询认证,结果如下图所示。 至此,一个完整集成TSDB到Istio可观性Prometheus服务的过程已经结束。 总结 在阿里云Kubernetes容器服务基础之上,快速搭建一套用于连接、管理以及安全化微服务的开放平台Istio,为应用引入和配置多个相关服务。使用Prometheus进行监控是Istio提供的监控能力之一,通过集成TSDB有效地解决持久化存储的问题。
概述 Knative Serving是一种可缩放至零、请求驱动的计算运行环境,构建在 Kubernetes 和 Istio 之上,支持为 serverless 应用、函数提供部署与服务。Knative Serving的目标是为Kubernetes提供扩展功能,用于部署和运行无服务器工作负载。 下面讲述一下在阿里云Kubernetes容器服务基础之上,如何快速搭建一套Knative Serving环境并进行自动扩容的体验。 搭建Knative Serving 1.准备Kubernetes环境 阿里云容器服务Kubernetes 1.11.5目前已经上线,可以通过容器服务管理控制台非常方便地快速创建 Kubernetes 集群。具体过程可以参考创建Kubernetes集群。 2.部署Istio Knative serving运行需要基于Istio,目前阿里云容器服务Kubernetes已提供了快速一键部署的方式来安装配置Istio。具体过程可以参考部署Istio。登录 容器服务管理控制台,单击左侧导航栏中的集群,进入集群列表页面。选择所需的集群并单击操作列更多 > 部署Istio。根据需要进行配置,然后点击部署按钮。稍等几十秒钟之后,Istio环境就可以部署完毕,可以通过查看Pod运行状态进行确认,如下所示。 3.部署Istio IngressGateway 登录容器服务管理控制台,点击左侧的应用目录,在右侧选中ack-istio-ingressgateway,如下: 点击参数, 可以通过修改参数配置进行定制化,默认参数提供了Istio IngressGateway的配置项,然后点击创建按钮。 查看命令空间 istio-system 下的Pod列表,确认运行状态,如下所示。 4.部署Knative CRD 登录容器服务管理控制台,点击左侧的应用目录,在右侧选中ack-knative-init,如下: 点击创建按钮安装Knative初始化所需的内容,包括安装CRD等。 5.部署Knative Serving 登录容器服务管理控制台,点击左侧的应用目录,在右侧选中ack-knative-serving,如下:点击参数, 可以通过修改参数配置进行定制化,默认参数提供了使用Istio IngressGateway的配置项,然后点击创建按钮。 至此,安装Knative Serving所需的4个Helm chart都已经安装完毕,如下所示: 体验Knative 安装autoscale示例 通过执行如下命令部署示例自动伸缩应用程序的 Knative Service: kubectl create -f autoscale.yaml 其中autoscale.yaml文件内容如下所示: apiVersion: serving.knative.dev/v1alpha1 kind: Service metadata: name: autoscale-go namespace: default spec: runLatest: configuration: revisionTemplate: metadata: annotations: # Target 10 in-flight-requests per pod. autoscaling.knative.dev/target: "10" autoscaling.knative.dev/class: kpa.autoscaling.knative.dev spec: container: image: registry.cn-beijing.aliyuncs.com/wangxining/autoscale-go:0.1 访问autoscale示例 找到入口主机名和IP并导出为环境变量: export IP_ADDRESS=`kubectl get svc istio-ingressgateway --namespace istio-system --output jsonpath="{.status.loadBalancer.ingress[*].ip}"` 向自动伸缩应用程序发出请求,并检查消耗资源情况: curl --header "Host: autoscale-go.default.{domain.name}" "http://${IP_ADDRESS?}?sleep=100&prime=10000&bloat=5" 注意,替换你的域名后缀替换{domain.name}。默认示例中为 aliyun.com。 curl --header "Host: autoscale-go.default.aliyun.com" "http://${IP_ADDRESS?}?sleep=100&prime=10000&bloat=5" Allocated 5 Mb of memory. The largest prime less than 10000 is 9973. Slept for 100.16 milliseconds. 通过以下命令安装负载生成器: go get -u github.com/rakyll/hey 维持50个并发请求,发送30秒的流量: hey -z 30s -c 50 \ -host "autoscale-go.default.aliyun.com" \ "http://${IP_ADDRESS?}?sleep=100&prime=10000&bloat=5" \ && kubectl get pods 可以看到运行30秒的流量请求状态,Knative服务随着请求数量的增大也自动扩容: Summary: Total: 30.1126 secs Slowest: 2.8528 secs Fastest: 0.1066 secs Average: 0.1216 secs Requests/sec: 410.3270 Total data: 1235134 bytes Size/request: 99 bytes Response time histogram: 0.107 [1] | 0.381 [12305] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.656 [0] | 0.930 [0] | 1.205 [0] | 1.480 [0] | 1.754 [0] | 2.029 [0] | 2.304 [0] | 2.578 [27] | 2.853 [23] | Latency distribution: 10% in 0.1089 secs 25% in 0.1096 secs 50% in 0.1107 secs 75% in 0.1122 secs 90% in 0.1148 secs 95% in 0.1178 secs 99% in 0.1318 secs Details (average, fastest, slowest): DNS+dialup: 0.0001 secs, 0.1066 secs, 2.8528 secs DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0000 secs req write: 0.0000 secs, 0.0000 secs, 0.0023 secs resp wait: 0.1214 secs, 0.1065 secs, 2.8356 secs resp read: 0.0001 secs, 0.0000 secs, 0.0012 secs Status code distribution: [200] 12356 responses NAME READY STATUS RESTARTS AGE autoscale-go-00001-deployment-5fb497488b-2r76v 2/2 Running 0 29s autoscale-go-00001-deployment-5fb497488b-6bshv 2/2 Running 0 2m autoscale-go-00001-deployment-5fb497488b-fb2vb 2/2 Running 0 29s autoscale-go-00001-deployment-5fb497488b-kbmmk 2/2 Running 0 29s autoscale-go-00001-deployment-5fb497488b-l4j9q 1/2 Terminating 0 4m autoscale-go-00001-deployment-5fb497488b-xfv8v 2/2 Running 0 29s 总结 在阿里云Kubernetes容器服务基础之上,可以快速搭建一套Knative Serving环境并进行自动扩容的体验。欢迎大家使用阿里云上的容器服务,快速搭建Knative环境,比较简单地集成到自己项目开发中。
Istio Gateway提供多个自定义入口网关的支持能力,通过开放一系列端口用于承载网格边缘的进入连接,同时可以使用不同loadbalancer来隔离不同的入口流量。cert-manager可用于使用存储在Kubernetes Secret资源中的任意签名密钥对来获取证书。本文提供了手动创建自定义入口网关的步骤说明,以及在该网关中如何使用cert-manager实现自动配置证书。 生成签名密钥对 CA Issuer不会自动创建和管理签名密钥对,要么用户自己提供,要么通过诸如OpenSSL的工具生成一个自签名CA的新签名密钥对。例如,通过如下命令可以生成x509类型的密钥和证书: # Generate a CA private key $ docker run -it -v $(pwd):/export frapsoft/openssl genrsa -out /export/ca.key 2048 # Create a self signed Certificate, valid for 10yrs with the 'signing' option set $ docker run -it -v $(pwd):/export frapsoft/openssl req -x509 -new -nodes -key /export/ca.key -subj "/CN=${COMMON_NAME}" -days 3650 -reqexts v3_req -extensions v3_ca -out /export/ca.crt 这些命令的输出将是两个文件,ca.key以及ca.crt签名密钥对的密钥和证书。如果你已经有了自己的密钥对,你应该将私钥和证书分别命名为ca.key与ca.crt。 将签名密钥对保存为Secret 我们将创建一个将使用此密钥对生成签名证书的颁发者Issuer,为了允许颁发者Issuer引用我们的密钥对,我们将其存储在Kubernetes Secret资源中。颁发者Issuer是命名空间资源,因此他们只能在自己的命名空间中引用Secrets。因此,我们将密钥对放入与Issuer相同的名称空间中。当然也可以创建一个面向集群范围版本的ClusterIssuer。以下命令将在默认命名空间中创建包含签名密钥对的Secret: kubectl create secret tls ca-key-pair \ --cert=ca.crt \ --key=ca.key \ --namespace=default 准备K8s+Istio环境 阿里云容器服务Kubernetes 1.11.5目前已经支持 Istio 1.0.5的一键部署,可以通过容器服务管理控制台非常方便地快速创建 Kubernetes 集群以及部署Istio。具体过程可以参考创建Kubernetes集群、部署Istio。请注意,当前部署Istio之后并不会创建IngressGateway。 部署Istio-init 点击左侧的应用目录,在右侧选中ack-istio-init,在右侧选择对应的集群,同时可以看到命名空间已设定为 istio-system ,发布名称已设定为istio-init,然后点击部署。几秒钟之后,Istio CRD在集群中被创建出来。 通过应用目录简便部署Istio certmanager 点击左侧的应用目录,在右侧选中ack-istio-certmanager,在打开的页面中点击参数, 可以通过修改参数配置进行定制化(当前不需要进行额外修改,保持默认值即可),如下所示: 在右侧选择对应的集群,同时可以看到命名空间已设定为 istio-system ,发布名称已设定为istio-certmanager,然后点击部署。几秒钟之后,Istio certmanager发布就可以创建出来,如下图所示容器组certmanager的启动日志:可以看到certmanager已经成功启动。 创建引用Secret的Issuer 现在可以创建一个引用我们刚刚创建的Secret资源的颁发者Issuer: kubectl apply -f - <<EOF apiVersion: certmanager.k8s.io/v1alpha1 kind: Issuer metadata: name: ca-issuer namespace: default spec: ca: secretName: ca-key-pair EOF 接下来准备获得证书! 获得签名证书 现在可以创建以下证书资源,该资源指定所需的证书。为了使用Issuer获取证书,我们必须在与Issuer相同的命名空间中创建Certificate资源,因为Issuer是命名空间资源,如本例所示。如果我们想要跨多个名称空间重用签名密钥对,那么就可以使用一个集群ClusterIssuer。 首先通过以下命令为域名myexample.com创建证书: kubectl apply -f - <<EOF apiVersion: certmanager.k8s.io/v1alpha1 kind: Certificate metadata: name: myexample-certificate namespace: default spec: secretName: istio-myexample-customingressgateway-certs issuerRef: name: ca-issuer # 可以通过引用ClusterIssuer类型的颁发者Issuer;默认情况使用只适用于命名空间的Issuer kind: Issuer commonName: myexample.com organization: - MyExample CA dnsNames: - myexample.com - www.myexample.com EOF 记下secretName因为接下来的步骤会需要引用它。 创建证书资源后,cert-manager将尝试使用颁发者ca-issuer获取证书。如果成功,证书将存储在与证书资源相同的命名空间(default)中的Secret资源istio-myexample-customingressgateway-certs中。 检查证书与密钥 由于我们已指定commonName字段,因此myexample.com将是证书的通用名称,并且通用名称和dnsNames阵列的所有元素都将是主题备用名称 (SAN)。如果我们没有指定公共名称,那么dnsNames列表的第一个元素 将用作公共名称,dnsNames列表的所有元素 也将是SAN。 创建上述证书后,我们可以检查是否已成功获取,如下所示查看了证书myexample-certificate: kubectl describe certificate myexample-certificate Name: myexample-certificate Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"certmanager.k8s.io/v1alpha1","kind":"Certificate","metadata":{"annotations":{},"name":"myexample-certificate","namespace":"... API Version: certmanager.k8s.io/v1alpha1 Kind: Certificate Metadata: Creation Timestamp: 2019-01-14T08:38:20Z Generation: 1 Resource Version: 19727 Self Link: /apis/certmanager.k8s.io/v1alpha1/namespaces/default/certificates/myexample-certificate UID: bf47b776-17d7-11e9-bafe-00163e069e12 Spec: Common Name: myexample.com Dns Names: myexample.com www.myexample.com Issuer Ref: Kind: Issuer Name: ca-issuer Organization: MyExample CA Secret Name: istio-myexample-customingressgateway-certs Status: Conditions: Last Transition Time: 2019-01-14T08:38:22Z Message: Certificate issued successfully Reason: CertIssued Status: True Type: Ready Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal IssueCert 80s cert-manager Issuing certificate... Normal CertIssued 80s cert-manager Certificate issued successfully 最后一行显示了证书成功被创建。 您还可以检查Issuer是否成功,应该看到base64编码的签名TLS密钥对。 kubectl get secret istio-myexample-customingressgateway-certs -oyaml 获得证书后,cert-manager将继续检查其有效性,并在接近到期时尝试更新。当证书上的“Not After”字段小于当前时间之后30天时,cert-manager认为证书即将到期。对于基于CA的颁发者,cert-manager将颁发证书,其中“Not After”字段设置为当前时间加上365天。 部署自定义网关 Gateway描述了在网格边缘操作的负载均衡器,用于接收传入或传出的HTTP / TCP连接。 点击左侧的应用目录,在右侧选中ack-istio-ingressgateway,在打开的页面中点击参数, 将在67行附近的名为istio-ingressgateway-certs的secretName修改为上述创建出的 istio-myexample-customingressgateway-certs 。修改如下如下所示: 在右侧选择对应的集群,同时选择与保密字典istio-myexample-customingressgateway-certs相同的命名空间即上文中设定的default ,发布名称设定为myexample-customingressgateway,然后点击部署。几秒钟之后,自定义的Istio 网关发布就可以创建出来。其中网关配置设置代理以充当负载平衡器,为入口公开端口80和443(https)。如下图所示: 定义内部服务 本示例中的内部服务是基于nginx实现的,首先为 NGINX 服务器创建配置文件。以域名myexample.com的内部服务为例,定义请求根路径直接返回字样"Welcome to myexample.com! This is one custom Istio Ingress Gateway powered by cert-manager!"及状态码200。 myexample-nginx.conf的具体内容如下: events { } http { log_format main '$remote_addr - $remote_user [$time_local] $status ' '"$request" $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error.log; server { listen 80; location / { return 200 'Welcome to myexample.com! This is one custom Istio Ingress Gateway powered by cert-manager!'; add_header Content-Type text/plain; } } } 创建 Kubernetes ConfigMap 存储 NGINX 服务器的配置: kubectl create configmap myexample-nginx-configmap --from-file=nginx.conf=./myexample-nginx.conf 设置命名空间default,启用sidecar自动注入: kubectl label namespace default istio-injection=enabled 注意: 确保该sidecar自动注入的Label需要在IngressGateway创建之后再进行标注,以确保IngressGateway不会自动注入。或者不启用自动注入,通过手工注入完成,具体参见手工注入。 部署 NGINX 服务器,创建域名myexample.com的内部服务: kubectl apply -f - <<EOF apiVersion: v1 kind: Service metadata: name: myexampleapp labels: app: myexampleapp spec: ports: - port: 80 protocol: TCP selector: app: myexampleapp --- apiVersion: apps/v1 kind: Deployment metadata: name: myexampleapp spec: selector: matchLabels: app: myexampleapp replicas: 1 template: metadata: labels: app: myexampleapp spec: containers: - name: nginx image: nginx ports: - containerPort: 80 volumeMounts: - name: nginx-config mountPath: /etc/nginx readOnly: true volumes: - name: nginx-config configMap: name: myexample-nginx-configmap EOF 创建自定义网关配置对象 以域名myexample.com为例,创建Istio自定义网关配置对象,如下所示: kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: annotations: name: istio-myexample-customingressgateway namespace: default spec: selector: istio: ingressgateway servers: - hosts: - '*.myexample.com' port: name: http number: 80 protocol: HTTP tls: httpsRedirect: true - hosts: - '*.myexample.com' port: name: https number: 443 protocol: HTTPS tls: mode: SIMPLE privateKey: /etc/istio/ingressgateway-certs/tls.key serverCertificate: /etc/istio/ingressgateway-certs/tls.crt EOF 创建VirtualService 同样地,接下来以域名myexample.com为例,创建链接到istio-myexample-customingressgateway的VirtualService: kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: istio-myexample-customvirtualservice spec: hosts: - "www.myexample.com" gateways: - istio-myexample-customingressgateway http: - route: - destination: host: myexampleapp port: number: 80 EOF 通过网关访问服务 以域名myexample.com为例,获取对应的自定义网关服务的公网IP地址,执行以下命令获取: kubectl get svc -l istio=ingressgateway NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE istio-ingressgateway LoadBalancer 172.19.12.75 106.14.48.121 80:31144/TCP,443:30441/TCP 11m 设置INGRESS_HOST 以及 SECURE_INGRESS_PORT 这两个环境变量,确定它们的正确取值,即替换成你实际环境的地址值: INGRESS_HOST=106.14.48.121 SECURE_INGRESS_PORT=443 检查 istio-ingressgateway Pod 是否正确的加载了证书和私钥: kubectl exec -it -n default $(kubectl -n default get pods -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}') -- ls -al /etc/istio/ingressgateway-certs tls.crt 和 tls.key 都应该保存在这个目录中。 检查 Ingress gateway 证书中的 Subject 字段的正确性: kubectl exec -i -n default $(kubectl get pod -l istio=ingressgateway -n default -o jsonpath='{.items[0].metadata.name}') -- cat /etc/istio/ingressgateway-certs/tls.crt | openssl x509 -text -noout | grep 'Subject:' Subject: O=MyExample CA, CN=myexample.com 检查 Ingress gateway 的代理能够正确访问证书: kubectl exec -ti $(kubectl get po -l istio=ingressgateway -n default -o jsonpath={.items[0]..metadata.name}) -n default -- curl 127.0.0.1:15000/certs { "ca_cert": "", "cert_chain": "Certificate Path: /etc/istio/ingressgateway-certs/tls.crt, Serial Number: c181438895a781c98759fb56b9cc1508, Days until Expiration: 364" } 至此,使用cert-manager部署自定义入口网关的所有步骤已完成。通过 HTTPS 协议访问 myexample.com 服务,即curl 发送 https 请求到istio-myexample-customingressgateway: curl -k -HHost:www.myexample.com --resolve www.myexample.com:443:106.14.48.121 https://www.myexample.com Welcome to myexample.com! This is one custom Istio Ingress Gateway powered by cert-manager! 回顾一下,获得证书后cert-manager将继续检查其有效性,并在接近到期时尝试更新。当证书上的“Not After”字段小于当前时间之后30天时,cert-manager认为证书即将到期。对于基于CA的颁发者,cert-manager将颁发证书,其中“Not After”字段设置为当前时间加上365天。
CoreDNS 及其Plugin扩展 CoreDNS是一个CNCF下的孵化级项目,它的前身是SkyDNS,主要目的是构建一个快速灵活的 DNS 服务器,让用户可以通过不同方式访问和使用 DNS 内的数据。基于 Caddy 服务器框架,CoreDNS 实现了一个插件链的架构,将大量逻辑抽象成插件Plugin的形式暴露给使用者,每个插件都执行DNS功能,例如Kubernetes 的 DNS 服务发现、Prometheus 监控等。 除了插件化之外,CoreDNS还有以下特性: 配置简单化: 引入表达力更强的 DSL,即 Corefile 形式的配置文件(也是基于 Caddy 框架开发); 一体化的解决方案:区别于 kube-dns,CoreDNS 编译出来就是一个单独的二进制可执行文件,内置了 cache,backend storage ,health check 等功能,无需第三方组件来辅助实现其他功能,从而使得部署更方便,内存管理更为安全; Corefile 介绍 Corefile 是 CoreDNS 的配置文件(源于 Caddy 框架的配置文件 Caddyfile),定义了以下内容: 服务器以什么协议监听在哪个端口(可以同时定义多个 server 监听不同端口) 服务器负责哪个 zone 的 authoritative DNS 解析 服务器将加载哪些插件 一个典型的 Corefile 格式如下所示: ZONE:[PORT] { [PLUGIN] ... } 其中, ZONE:用于定义服务器负责的 zone; PORT: 监听端口,可选项,默认为 53; PLUGIN:定义服务器所要加载的插件plugin,每个 plugin 可以有多个参数; Plugin工作机制 当 CoreDNS 启动后,它将根据配置文件启动不同 server ,每台 server 都拥有自己的插件链。当有 DNS 请求时,它将依次经历如下 3 步逻辑: 如果有当前请求的 server 有多个 zone,将采用贪心原则选择最匹配的 zone; 一旦找到匹配的 server,按照 plugin.cfg 定义的顺序执行插件链上的插件; 每个插件将判断当前请求是否应该处理,将有以下几种可能: 请求被当前插件处理 插件将生成对应的响应并回给客户端,此时请求结束,下一个插件将不会被调用,如 whoami 插件; 请求不被当前插件处理 直接调用下一个插件。如果最后一个插件执行错误,服务器返回 SERVFAIL 响应; 请求被当前插件以 Fallthrough 形式处理 如果请求在该插件处理过程中有可能将跳转至下一个插件,该过程称为 fallthrough,并以关键字 fallthrough 来决定是否允许此项操作,例如 host 插件,当查询域名未位于 /etc/hosts,则调用下一个插件; 请求在处理过程被携带 Hint 请求被插件处理,并在其响应中添加了某些信息(hint)后继续交由下一个插件处理。这些额外的信息将组成对客户端的最终响应,如 metric 插件; CoreDNS与Kubernetes 从Kubernetes 1.11开始,CoreDNS作为Kubernetes的DNS插件进入GA状态,Kubernetes推荐使用CoreDNS作为集群内的DNS服务。在阿里云容器服务Kubernetes 1.11.5及更新版本中默认安装使用CoreDNS作为DNS服务。通过如下命令可以查看CoreDNS配置信息: # kubectl -n kube-system get configmap coredns -oyaml apiVersion: v1 data: Corefile: |- .:53 { errors health kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure upstream fallthrough in-addr.arpa ip6.arpa } prometheus :9153 proxy . /etc/resolv.conf cache 30 reload } kind: ConfigMap metadata: creationTimestamp: "2018-12-28T07:28:34Z" name: coredns namespace: kube-system resourceVersion: "2453430" selfLink: /api/v1/namespaces/kube-system/configmaps/coredns uid: 2f3241d5-0a72-11e9-99f1-00163e105bdf 配置文件各参数的含义如下: 名称 含义 errors 错误会被记录到标准输出 health 健康状况,可以通过http://localhost:8080/health查看 kubernetes 根据服务的IP响应DNS查询请求,Cluster Domain默认为cluster.local prometheus 可以通过http://localhost:9153/metrics获取prometheus格式的监控数据 proxy 本地无法解析后,向上级地址进行查询,默认使用宿主机的 /etc/resolv.conf 配置 cache 缓存时间 通过应用目录简便部署Istio CoreDNS 阿里云容器服务Kubernetes 1.11.5目前已经上线,可以通过容器服务管理控制台非常方便地快速创建 Kubernetes 集群。具体过程可以参考创建Kubernetes集群。 点击左侧的应用目录,在右侧选中ack-istio-coredns,在打开的页面中点击参数, 可以通过修改参数配置进行定制化,如下所示: 参数描述如下: 参数 描述 值 默认 replicaCount 指定副本数 number 1 coreDNSImage 指定coredns镜像名称 valid image tag coredns/coredns:1.2.2 coreDNSPluginImage 指定插件镜像名称 valid image name registry.cn-hangzhou.aliyuncs.com/aliacs-app-catalog/istio-coredns-plugin:v1.0-istio-1.1 修改之后,在右侧选择对应的集群、命名空间 istio-system 以及发布名称 istio-coredns,然后点击部署。几秒钟之后,Istio CoreDNS发布就可以创建出来,如下图所示: 更改集群CoreDNS配置 可以通过执行以下命令来获取 istiocoredns服务的Cluster IP: # kubectl get svc istiocoredns -n istio-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE istiocoredns ClusterIP 172.19.10.196 <none> 53/UDP,53/TCP 44m 更新集群的coredns的配置项configmap, 将此istiocoredns服务作为 *.global 域的上游DNS服务,在此配置项configmap中添加域 *.global,如下所示: apiVersion: v1 kind: ConfigMap metadata: name: coredns namespace: kube-system data: Corefile: |- .:53 { errors health kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure upstream fallthrough in-addr.arpa ip6.arpa } prometheus :9153 proxy . /etc/resolv.conf cache 30 reload } global:53 { errors cache 30 proxy . {replace this with the actual cluster IP of this istiocoredns service} } 更改此配置项后,集群的CoreDNS容器会重新加载这些配置内容,可以通过如下命令行查看加载日志: # kubectl get -n kube-system pod | grep coredns coredns-8645f4b4c6-5frkg 1/1 Running 0 20h coredns-8645f4b4c6-lj59t 1/1 Running 0 20h # kubectl logs -f -n kube-system coredns-8645f4b4c6-5frkg .... 2019/01/08 05:06:47 [INFO] Reloading 2019/01/08 05:06:47 [INFO] plugin/reload: Running configuration MD5 = 05514b3e44bcf4ea805c87cc6cd56c07 2019/01/08 05:06:47 [INFO] Reloading complete # kubectl logs -f -n kube-system coredns-8645f4b4c6-lj59t .... 2019/01/08 05:06:31 [INFO] Reloading 2019/01/08 05:06:32 [INFO] plugin/reload: Running configuration MD5 = 05514b3e44bcf4ea805c87cc6cd56c07 2019/01/08 05:06:32 [INFO] Reloading complete 创建ServiceEntry验证DNS解析 ServiceEntry 能够在 Istio 内部的服务注册表中加入额外的条目,从而让网格中自动发现的服务能够访问和路由到这些手工加入的服务。ServiceEntry 描述了服务的属性,即包括DNS 名称、虚拟IP、端口、协议以及端点等。这类服务可能是网格外的 API,或者是处于网格内部但却不存在于平台的服务注册表中的条目,例如需要和 Kubernetes 服务沟通的一组基于虚拟机VM提供的服务。 下面的例子中,使用了通配符定义 hosts,并指定了address,如下所示: apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: external-svc-serviceentry spec: hosts: - '*.test.global' addresses: - 127.255.0.2 ports: - number: 8080 name: http1 protocol: HTTP location: MESH_INTERNAL resolution: DNS endpoints: - address: 47.111.38.80 ports: http1: 15443 执行如下命令查看容器 istiocoredns ,可以看到上述ServiceEntry的域名映射关系已经被加载: # kubectl get po -n istio-system | grep istiocoredns istiocoredns-cdc56b67-ngtkr 2/2 Running 0 1h # kubectl logs --tail 2 -n istio-system istiocoredns-cdc56b67-ngtkr -c istio-coredns-plugin 2019-01-08T05:27:22.897845Z info Have 1 service entries 2019-01-08T05:27:22.897888Z info adding DNS mapping: .test.global.->[127.255.0.2] 使用镜像 tutum/dnsutils 创建测试容器: kubectl run dnsutils -it --image=tutum/dnsutils bash 进入容器命令行之后,执行dig 查看相应的域名解析: root@dnsutils-d485fdbbc-8q6mp:/# dig +short 172.19.0.10 A service1.test.global 127.255.0.2 root@dnsutils-d485fdbbc-8q6mp:/# dig +short 172.19.0.10 A service2.test.global 127.255.0.2 总结 Istio支持几种不同的拓扑结构,用于在单个集群之外分发应用程序的服务,例如在服务网格中的服务可以使用 ServiceEntry 来访问独立的外部服务或访问由另一个服务网格公开的服务(这种情况通常称之为网格联合)。使用Istio CoreDNS为远程集群中的服务提供DNS解析,将允许现有的应用程序不需要修改就可以如同访问本集群内的服务一样。
流量镜像 流量镜像,也称为影子流量,流量镜像提供一种尽可能低的风险为生产带来变化的强大功能。镜像会将实时流量的副本发送到镜像服务。镜像流量发生在主服务的关键请求路径之外。 在非生产或者测试环境中,尝试访问一个服务所有可能的测试用例组合是个非常不现实的任务。 在某些情况下,编写这些用例的所有工作也可能与实际生产所需的用例不匹配。在理想情况下,可以使用实时的生产用例和流量来帮助完善在测试环境中错过的功能区域。 一旦我们能够可靠地镜像流量,就可以开始做一些有价值的事情,例如通过请求流量对比工具Diffy,可以将引入测试集群的流量与生产集群中的预期行为进行比较。例如,我们可能想比较请求结果与预期结果间的偏差,或是API协议中的数据损坏情况,以便更好地兼容。除此之外,需要注意: 当流量镜像到不同的服务时,会发生在请求的关键路径之外; 忽略对任何镜像流量的响应; 流量被视为“即发即忘”; 流量对比 此处,插入一个代理就可以负责此类流量的协调,并对其进行有趣的比较。Diffy就是一款这样的代理工具。Diffy启动一个代理服务(例如监听端口8880),再根据用户设置的primary、secondary两个旧服务地址(primary和secondary代码完全相同,目的是为了减少噪音干扰)、candidate新服务地址。 它还能够检测结果中的噪音,并通过先调用两个实时服务的实例来忽略它们(例如时间戳,单调递增计数器等提示),总结来说就是检测,然后在测试服务中忽略掉这部分。 Diffy还提供了一个不错的页面可以用来查看调用结果、对比情况、和基于某些特征的过滤。它还有一个很好的管理控制台,可以查看有关调用比较结果的功能指标(metrics)和统计数据(statistics)。 创建用于Istio流量镜像的服务 在此任务中,将首先强制所有流量到 v1 版本的服务。然后,将使用规则将一部分流量镜像到 v2版本。 首先部署两个版本的示例服务。 版本1的部署使用了Docker镜像httpbin,提供常见的http请求访问: apiVersion: extensions/v1beta1 kind: Deployment metadata: name: mirrorservice-sample-v1 spec: replicas: 1 template: metadata: labels: app: mirrorservice-sample version: v1 spec: containers: - image: docker.io/kennethreitz/httpbin imagePullPolicy: IfNotPresent name: mirrorservice-sample command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:44134", "httpbin:app"] ports: - containerPort: 44134 版本2的部署使用了自定义的Docker镜像,对应的Dockerfile如下: FROM nginx:latest COPY default.conf /etc/nginx/conf.d/ EXPOSE 80 所需的nginx 配置文件: server { listen 44134; server_name localhost; location / { proxy_pass http://httpbin-diffy.diffy:8880/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } } 版本2的部署作为Istio的流量镜像目标,在接收到流量之后会转发到Diffy的代理中。当前没有直接将Diffy代理作为Isito流量镜像目标,原因是Diffy代理与Envoy代理目前本身有冲突,无法正常流量转发,因此需要此部署中转一下。 apiVersion: extensions/v1beta1 kind: Deployment metadata: name: mirrorservice-sample-v2 spec: replicas: 1 template: metadata: labels: app: mirrorservice-sample version: v2 spec: containers: - name: mirrorservice-sample image: registry.cn-beijing.aliyuncs.com/wangxining/mirrorservice:0.1 imagePullPolicy: Always ports: - containerPort: 44134 对应的Kubernetes service: apiVersion: v1 kind: Service metadata: name: mirrorservice-sample spec: type: ClusterIP ports: - name: http port: 44134 selector: app: mirrorservice-sample 创建流量镜像的Istio策略 默认情况下,Kubernetes 在服务的两个版本之间进行负载均衡。创建如下流量镜像规则将 100% 的流量发送到 v1, 同时指定流量镜像到v2。当流量被镜像时,请求将通过其主机/授权报头发送到镜像服务附上 -shadow 。 apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: mirrorservice-sample spec: host: mirrorservice-sample subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: mirrorservice-sample spec: hosts: - mirrorservice-sample http: - route: - destination: host: mirrorservice-sample subset: v1 weight: 100 #- destination: # host: mirrorservice-sample # subset: v2 # weight: 0 mirror: host: mirrorservice-sample subset: v2 搭建Diffy用于请求流量对比 Diffy可以作为代理,截取请求并发送至所有运行的服务实例,通过对比响应结果来发现每次迭代代码中可能存在的问题。其中,Diffy上运行了三类代码实例: 线上稳定版本:一个运行线上稳定版本代码的节点 线上稳定版本备份:同样运行了线上的稳定版本,用于消除噪音 测试版本:待上线的测试版本,用于和线上环境代码进行对比 在实际Diffy测试中,会发现大部分的接口都会有一定差异,原因是这些响应中存在了噪音,噪音可能包括: server响应中生成的时间戳 随机生成的数字 系统服务间的有条件竞争 Diffy能够通过一定的方式,清除这类噪音,保证分析结果不被影响。 创建Diffy及示例服务 通过以下YAML创建Diffy服务: apiVersion: v1 kind: Service metadata: name: httpbin-diffy labels: app: httpbin-diffy spec: ports: - name: http-proxy port: 8880 - name: http-admin port: 8881 - name: http-console port: 8888 selector: app: httpbin-diffy --- apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: app: httpbin-diffy version: v2 name: httpbin-diffy-v2 spec: replicas: 1 selector: matchLabels: app: httpbin-diffy version: v2 template: metadata: labels: app: httpbin-diffy version: v2 spec: containers: - image: lordofthejars/diffy:1.0 imagePullPolicy: IfNotPresent livenessProbe: exec: command: - curl - localhost:8888 initialDelaySeconds: 10 periodSeconds: 60 timeoutSeconds: 1 name: httpbin-diffy args: ["-candidate=httpbin-candidate:8080", "-master.primary=httpbin-master:8080", "-master.secondary=httpbin-master:8080", "-service.protocol=http", "-serviceName=httpbin", "-proxy.port=:8880", "-admin.port=:8881", "-http.port=:8888", "-rootUrl='localhost:8888'"] ports: - containerPort: 8888 name: http-console protocol: TCP - containerPort: 8880 name: http-proxy protocol: TCP - containerPort: 8881 name: http-admin protocol: TCP readinessProbe: exec: command: - curl - localhost:8888 initialDelaySeconds: 10 periodSeconds: 60 timeoutSeconds: 1 securityContext: privileged: false 通过以下YAML创建示例所用的primary、secondary(当前示例中与primary相同)与candidate服务: apiVersion: v1 kind: Service metadata: name: httpbin-master labels: app: httpbin-master spec: ports: - name: http port: 8080 selector: app: httpbin version: v1 --- apiVersion: v1 kind: Service metadata: name: httpbin-candidate labels: app: httpbin-candidate spec: ports: - name: http port: 8080 selector: app: httpbin version: v2 --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: httpbin-v1 spec: replicas: 1 template: metadata: labels: app: httpbin version: v1 spec: containers: - image: docker.io/kennethreitz/httpbin imagePullPolicy: IfNotPresent name: httpbin command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:8080", "httpbin:app"] ports: - containerPort: 8080 --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: httpbin-v2 spec: replicas: 1 template: metadata: labels: app: httpbin version: v2 spec: containers: - image: docker.io/kennethreitz/httpbin imagePullPolicy: IfNotPresent name: httpbin command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:8080", "httpbin:app"] ports: - containerPort: 8080 发送流量进行镜像验证 启动 sleep 服务,这样就可以使用 curl 来提供负载: cat <<EOF | istioctl kube-inject -f - | kubectl create -f - apiVersion: extensions/v1beta1 kind: Deployment metadata: name: sleep spec: replicas: 1 template: metadata: labels: app: sleep spec: containers: - name: sleep image: tutum/curl command: ["/bin/sleep","infinity"] imagePullPolicy: IfNotPresent EOF 进入到SLEEP_POD, 具体POD名称根据实际赋值。 kubectl exec -it $SLEEP_POD -c sleep sh 发送流量: curl -v http://mirrorservice-sample:44134/headers 可以查看 v1的访问日志记录,如下所示创建的请求100%指向了v1。 与此同时,查看Diffy的Web界面,可以看到创建的请求也被镜像到Diffy Proxy: Diffy能够通过一定的方式,清除这类噪音,保证分析结果不被影响。 结论 流量镜像提供一种尽可能低的风险为生产带来变化的强大功能。镜像会将实时流量的副本发送到镜像服务,镜像流量发生在主服务的关键请求路径之外。一旦我们能够可靠地镜像流量,就可以开始做一些有价值的事情,例如通过请求流量对比工具Diffy,可以将引入测试集群的流量与生产集群中的预期行为进行比较。 支持流量镜像只是 Istio 的众多功能之一,它将使基于大型微服务的应用程序的生产部署与管理变得更加简单。欢迎大家使用阿里云上的容器服务,快速搭建微服务的开放治理平台Istio,比较简单地集成到自己项目的微服务开发中。
概述 在项目迭代的过程中,不可避免需要上线。上线对应着部署,或者重新部署;部署对应着修改;修改则意味着风险。 灰即在黑与白之间,灰度发布就是指能够平滑过渡的一种发布方式。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度,而我们平常所说的AB测试、金丝雀发布等也就是灰度发布的不同方式。 蓝绿发布 蓝绿发布是指不停老版本,部署新版本然后进行测试,确认没有问题之后,将流量切到新版本,然后老版本同时也升级到新版本。它的特点是无需停机,并且风险较小。 过程大致如下: 第一步、部署版本1的应用(一开始的状态),所有外部请求的流量都打到这个版本上。 第二步、部署版本2的应用,版本2的代码与版本1不同(新功能、Bug修复等)。 第三步、将流量从版本1切换到版本2,即流量从v1:v2为100:0,切换为0:100。 第四步,如果版本2存在问题,需要回滚到版本1,进行流量切换回v1:v2为100:0。 A / B测试 A/B 测试不同于蓝绿发布,它是用来测试应用功能表现的一种方法,例如可用性、受欢迎程度、可见性等等。 A/B 测试通常会针对特定的用户群体进行,其目的在于通过科学的实验设计、采样样本代表性、流量分割与小流量测试等方式来获得具有代表性的实验结论,并确信该结论在推广到全部流量可信;这与蓝绿发布的目的不尽相同,蓝绿发布主要是用于安全稳定地发布新版本应用,并在必要时回滚。 金丝雀发布 金丝雀发布是指通过让一小部分用户流量引入的新版本进行测试,如果一切顺利,则可以增加(可能逐渐增加)百分比,逐步替换旧版本。如在过程中出现任何问题,则可以中止并回滚到旧版本。最简单的方式,是随机选择百分比请求到金丝雀版本,但在更复杂的方案下,则可以基于请求的内容、特定范围的用户或其他属性等。 Kubernetes中的灰度发布 Kubernetes自带的rolling-update 功能提供了一种渐进式的更新过程,可以实现不中断业务的应用升级。简要回顾下Kubernetes的升级策略。 spec: replicas: 5 strategy: type: RollingUpdate rollingUpdate: maxSurge: 2 maxUnavailable: 2 minReadySeconds: 5 maxSurge: 升级过程中最多可以比原先设定所多出的pod 数量,此栏位可以为固定值或是比例(%)。例如:maxSurge: 1、replicas: 5,代表Kubernetes 会先开好1 个新pod 后才删掉一个旧的pod,整个升级过程中最多会有5+1 个pod。 maxUnavailable: 最多可以有几个pod 处在无法服务的状态,当maxSurge不为零时,此栏位亦不可为零。例如. maxUnavailable: 1,代表Kubernetes 整个升级过程中最多会有1 个pod 处在无法服务的状态。 minReadySeconds: 容器内应用程式的启动时间,Kubernetes 会等待设定的时间后才继续进行升级流程,如果没有此栏位的话,Kubernetes 会假设该容器一开完后即可进行服务。 实现滚动升级的方式也有以下几种: 使用kubectl set image 使用kubectl replace 使用kubectl rollout 尽管这种机制能够很好地工作,但只适用于部署的经过适当测试的版本,也就是说,更多的是蓝/绿发布场景。实际上,Kubernetes 中的金丝雀发布将使用具有相同Label的两个 Deployment 来完成。在这种情况下,我们不能再使用AutoScaler,因为是有由两个独立的AutoScaler来进行控制,不同负载情况下,replicas比例(百分比)可能与所需的比例不同。 无论我们使用一个或者两个部署,使用 Kubernetes 进行的金丝雀发布都存在一个根本问题:使用实例扩容来管理流量;这是因为版本流量分发和副本部署在Kubernetes平台中并非独立。所有 pod 副本,无论版本如何,在 kube-proxy 循环池中都被相同对待,因此管理特定版本接收的流量的唯一方法是控制副本比例。以小百分比例的金丝雀流量需要许多副本(例如1% 将需要至少 100 个副本)。即使我们可以忽略这个问题,部署方式功能仍然非常有限,因为它只支持简单(随机百分比)金丝雀部署。如果想根据某些特定规则将请求路由到金丝雀版本上,仍然需要另一种解决方案。这就是Istio的。 使用 Istio进行灰度发布 使用Istio的流量管理模型,本质上是将流量与基础设施扩容进行解耦,让运维人员可以通过Pilot指定流量遵循什么规则,而不是指定哪些pods/VM应该接收流量。通过将流量从基础设施扩展中解耦,就可以让 Istio 提供各种独立于应用程序代码之外的流量管理功能。 这些功能都是通过部署的Envoy sidecar代理来实现的。 在使用 Istio实现灰度发布的情况下,流量路由和副本部署是两个完全独立的功能。服务的 pod 数量可以根据流量负载灵活伸缩,与版本流量路由的控制完全无关。这在自动缩放的情况下能够更加简单地管理金丝雀版本。 Istio 的路由规则非常灵活,可以支持细粒度控制流量百分比(例如,路由 1% 的流量而不需要 100 个 pod),也可以使用其他规则来控制流量(例如,将特定用户的流量路由到金丝雀版本)。如下所示: apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: addedvalues spec: hosts: - addedvalues http: - match: - headers: end-user: prefix: yunqi route: - destination: host: addedvalues subset: v3 - route: - destination: host: addedvalues subset: v2 weight: 50 - destination: host: addedvalues subset: v1 weight: 50 独立于AutoScaler 使用 Istio进行灰度发布时,由于不再使用副本比例维持流量分发比例,所以可以安全地设置 Kubernetes HPA 来管理两个版本 Deployment 的副本。 统一的流量路由规则 无论是蓝绿发布,还是AB测试或金丝雀发布,基于Istio提供的统一的流量路由规则定义可以实现。具体如下: 基于流量比例的发布 Istio根据输入的流量比例来确定流量分发的比重。范围限制为[0, 100], 例如,当版本v1配置为0,版本v2配置为100时,这种场景即为蓝绿发布中使用到的规则。 此外,如果版本v1比例设置为x,则x%的服务流量会走向此版本v1,(100-x)%的流量会走向版本v2,即从版本v1分走一部分流量。这种场景即为金丝雀发布或AB测试中使用到的规则。 基于请求内容的发布 基于请求内容的发布会遍历除默认版本外的全部金丝雀规则,若满足某个版本的规则,则流量走向此版本,若全部不满足,则流量会走到默认版本。这种场景即为金丝雀发布或AB测试中使用到的规则。 总结 如上所述,Istio 可以支持灵活规则下的金丝雀发布,以及区别于Kubernetes 部署方式。Istio 服务网格提供了管理流量分配所需的基础控制,并完全独立于AutoScaler,从而允许简单而强大的方式来进行金丝雀测试和上线。 支持金丝雀部署的智能路由只是 Istio 的众多功能之一,它将使基于大型微服务的应用程序的生产部署变得更加简单。欢迎大家使用阿里云上的容器服务,快速搭建微服务的开放治理平台Istio,比较简单地集成到自己项目的微服务开发中。
概述 使用Istio的流量管理模型,本质上是将流量与基础设施扩容进行解耦,让运维人员可以通过Pilot指定流量遵循什么规则,而不是指定哪些pods/VM应该接收流量。通过将流量从基础设施扩展中解耦,就可以让 Istio 提供各种独立于应用程序代码之外的流量管理功能。 这些功能都是通过部署的Envoy sidecar代理来实现的。 在一个典型的网格中,通常有一个或多个用于终结外部 TLS 链接,将流量引入网格的负载均衡器(我们称之为 gateway),然后流量通过sidecar gateway流经内部服务。下图描绘了网关在网格中的使用情况: Istio Gateway 为 HTTP/TCP 流量配置了一个负载均衡,多数情况下在网格边缘进行操作,用于启用一个服务的入口(ingress)流量。网格中可以存在任意数量的 Gateway,并且多个不同的 Gateway 实现可以共存。 对于入口流量管理,可能会问为什么不直接使用 Kubernetes Ingress API ? 原因是 Ingress API 无法表达 Istio 的路由需求。 Ingress 试图在不同的 HTTP 代理之间取一个公共的交集,因此只能支持最基本的 HTTP 路由,最终导致需要将代理的其他高级功能放入到注解(annotation)中,而注解的方式在多个代理之间是不兼容的,无法移植。 此外,Kubernetes Ingress本身不支持TCP协议。因此,即使TCP不是NGINX的限制,也无法通过Ingress创建来配置NGINX Ingress Controller以进行TCP负载均衡。当然可以通过创建一个Kubernetes ConfigMap,来使用NGINX的TCP负载均衡功能,具体可参阅这里:Support for TCP/UDP Load Balancing 。可想而知,这种配置在多个代理中无法兼容与移植。 与Kubernetes Ingress 不同,Istio Gateway 通过将 L4-L6 配置与L7配置分离的方式克服了 Ingress 的上述缺点。 Gateway 只用于配置 L4-L6 功能(例如,对外公开的端口、TLS 配置),所有主流的代理均以统一的方式实现了这些功能。 然后,通过在 Gateway 上绑定 VirtualService 的方式,可以使用标准的 Istio 规则来控制进入 Gateway 的 HTTP 和 TCP 流量。 本文通过一个示例来讲述如何通过一个简单且标准的Istio规则来控制TCP入口流量的路由,从而实现TCP入口流量路由的统一管理。 准备Kubernetes集群 阿里云容器服务Kubernetes 1.11.2目前已经上线,可以通过容器服务管理控制台非常方便地快速创建 Kubernetes 集群。具体过程可以参考创建Kubernetes集群。 确保安装配置kubectl 能够连接上Kubernetes 集群。 部署Istio 打开容器服务控制台,在左侧导航栏中选中集群,右侧点击更多,在弹出的菜单中选中 部署Istio。 在打开的页面中可以看到Istio默认安装的命名空间、发布名称;通过勾选来确认是否安装相应的模块,默认是勾选前四项;第5项是提供基于日志服务的分布式跟踪能力,本示例中不启用。 点击 部署Istio 按钮,几十秒钟之后即可完成部署。 自动 Sidecar 注入 Istio就是充分利用了Kubernets Webhook机制实现Envoy Proxy Sidecar的自动注入。执行以下命令可以为命令空间default增加标签istio-injection且值设为enabled。 查看namespace: 点击编辑,为 default 命名空间打上标签 istio-injection=enabled。 TCP Server镜像 你可以直接使用已经构建好的镜像文件:registry.cn-hangzhou.aliyuncs.com/wangxining/tcptest:0.1。 或者按照以下步骤自行构建。 从以下地址克隆代码库: https://github.com/osswangxining/Istio-TCPRoute-Sample 切换到代码目录可以看到一个Dockerfile: 运行以下命令构建镜像,例如: docker build -t {镜像仓库地址} . 之后推送到你自己的镜像仓库地址。 部署应用 使用 kubectl 部署服务 cd k8s kubectl apply -f deployment.yml kubectl apply -f service.yml 上面的命令会创建1个Service(tcp-echo)与2个Deployment(tcp-echo-v1与tcp-echo-v2),其中这2个Deployment都包含了标签app: tcp-echo,并且Service(tcp-echo)对应到上述2个Deployment: selector: app: "tcp-echo" 确认TCPServer的POD启动: kubectl get pods --selector=app=tcp-echo NAME READY STATUS RESTARTS AGE tcp-echo-v1-7c775f57c9-frprp 2/2 Running 0 1m tcp-echo-v2-6bcfd7dcf4-2sqhf 2/2 Running 0 1m 确认TCPServer的服务: kubectl get service --selector=app=tcp-echo NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE tcp-echo ClusterIP 172.19.46.255 <none> 3333/TCP 17h 定义 gateway 使用如下命令创建2个Gateway,其中一个Gateway监听31400端口,另一个Gateway监听31401端口。 kubectl apply -f gateway.yaml gateway.yaml代码如下: apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: tcp-echo-gateway spec: selector: istio: ingressgateway # use istio default controller servers: - port: number: 31400 name: tcp protocol: TCP hosts: - "*" --- apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: tcp-echo-gateway-v2 spec: selector: istio: ingressgateway # use istio default controller servers: - port: number: 31401 name: tcp protocol: TCP hosts: - "*" 如下所示,这2个Gateway共用了一个ingressgateway服务,该ingressgateway使用了Loadbalancer方式暴露,可提供对外使用的IP地址。 创建Istio规则 kubectl apply -f destination-rule-all.yaml kubectl apply -f virtualservice.yaml 该示例规则中定义了2个subset(或者称之为版本),第一个Gateway会把请求到端口31400的TCP流量进行转发到版本v1的POD,第二个Gateway会把请求到端口31401的TCP流量进行转发到版本v2的POD。 apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: tcp-echo spec: hosts: - "*" gateways: - tcp-echo-gateway - tcp-echo-gateway-v2 tcp: - match: - port: 31400 gateways: - tcp-echo-gateway route: - destination: host: tcp-echo.default.svc.cluster.local subset: v1 port: number: 3333 - match: - port: 31401 gateways: - tcp-echo-gateway-v2 route: - destination: host: tcp-echo.default.svc.cluster.local subset: v2 port: number: 3333 体验TCP路由功能 查看Ingress Gateway的地址点击左侧导航栏中的服务,在右侧上方选择对应的集群和命名空间,在列表中找到istio-ingressgateway的外部端点地址。 打开终端,运行以下命令(前提是安装了nc): nc INGRESSGATEWAY_IP 31400 输入文字进行如下交互,可以看到该端口的TCP流量转发到了版本v1对应的POD: Welcome, you are connected to node cn-beijing.i-2zeij4aznsu1dvd4mj5c. Running on Pod tcp-echo-v1-7c775f57c9-frprp. In namespace default. With IP address 172.16.2.90. Service default. hello, app1 hello, app1 continue.. continue.. 查看版本v1的POD的日志: kubectl logs -f tcp-echo-v1-7c775f57c9-frprp -c tcp-echo-container | grep Received 2018/10/17 07:32:29 6c7f4971-40f1-4f72-54c4-e1462a846189 - Received Raw Data: [104 101 108 108 111 44 32 97 112 112 49 10] 2018/10/17 07:32:29 6c7f4971-40f1-4f72-54c4-e1462a846189 - Received Data (converted to string): hello, app1 2018/10/17 07:34:40 6c7f4971-40f1-4f72-54c4-e1462a846189 - Received Raw Data: [99 111 110 116 105 110 117 101 46 46 10] 2018/10/17 07:34:40 6c7f4971-40f1-4f72-54c4-e1462a846189 - Received Data (converted to string): continue.. 打开另外一个终端,运行以下命令: nc INGRESSGATEWAY_IP 31401 输入文字进行如下交互,可以看到该端口的TCP流量转发到了版本v2对应的POD: Welcome, you are connected to node cn-beijing.i-2zeij4aznsu1dvd4mj5b. Running on Pod tcp-echo-v2-6bcfd7dcf4-2sqhf. In namespace default. With IP address 172.16.1.95. Service default. hello, app2 hello, app2 yes,this is app2 yes,this is app2 查看版本v2的POD的日志: kubectl logs -f tcp-echo-v2-6bcfd7dcf4-2sqhf -c tcp-echo-container | grep Received 2018/10/17 07:36:29 1a70b9d4-bbc7-471d-4686-89b9234c8f87 - Received Raw Data: [104 101 108 108 111 44 32 97 112 112 50 10] 2018/10/17 07:36:29 1a70b9d4-bbc7-471d-4686-89b9234c8f87 - Received Data (converted to string): hello, app2 2018/10/17 07:36:37 1a70b9d4-bbc7-471d-4686-89b9234c8f87 - Received Raw Data: [121 101 115 44 116 104 105 115 32 105 115 32 97 112 112 50 10] 2018/10/17 07:36:37 1a70b9d4-bbc7-471d-4686-89b9234c8f87 - Received Data (converted to string): yes,this is app2 总结 本文通过一个示例来讲述如何通过一个简单且标准的Istio规则来控制TCP入口流量的路由,从而实现TCP入口流量路由的统一管理。 欢迎大家使用阿里云上的容器服务,快速搭建微服务的开放治理平台Istio,比较简单地集成到自己项目的微服务开发中。
概述 在前面的文章中,介绍了在阿里云容器服务上基于Istio如何实现东西向流量管理。 回顾下引用的示例: 该样例应用由四个单独的微服务构成,用来演示多种 Istio 特性。该应用模仿某银行金融产品的一个分类,显示某一金融产品的信息。页面上会显示该产品的描述、明细,以及针对特定用户的增值服务。 四个单独的微服务: productpage :productpage 微服务会调用 details 和 addedvalues两个微服务,用来生成页面。 details :该微服务包含了金融产品的信息。 addedvalues:该微服务包含了针对特定用户的增值服务。它还会调用 styletransfer微服务。 styletransfer:该微服务提供了转移照片艺术风格的API功能。 addedvalues微服务有 3 个版本: v1 版本不会调用 styletransfer 服务,也不会提供风险和投资分析结果。 v2 版本不会调用 styletransfer 服务,但会提供风险和投资分析结果。 v3 版本会调用 styletransfer 服务,提供针对特定用户的增值服务,即允许用户上传图片进行风格转换,并返回一张转换后的图片。 这是4个服务的调用关系,其中3个在Kubernetes集群内,另外一个在集群之外。 在前面文章中已经提到,第3 个版本的addedvalues微服务提供的页面中,按钮是disabled状态,无法点击。这是因为缺省情况下,Istio 服务网格内的 Pod,由于其 iptables 将所有外发流量都透明的转发给了 Sidecar,所以这些集群内的服务无法访问集群之外的 URL,而只能处理集群内部的目标。 出口流量管理 本任务描述了如何将外部服务暴露给 Istio 集群中的客户端。你将会学到如何通过定义 ServiceEntry 来调用外部服务; kubectl apply -f serviceentry.yaml 注销之后,当使用以yunqi开头的用户名再次登录时,就会看到如下页面内容, 按钮是enabled状态,可以点击。 点击按钮,在新弹出的窗口中,上次图片进行风格转换: 总结 本文通过示例演示了Istio如何实现从集群内访问外部服务的出口的流量管理。 欢迎大家使用阿里云上的容器服务,快速搭建微服务的开放治理平台Istio,比较简单地集成到自己项目的微服务开发中。
概述 使用 Ingress 将Kubernetes中的应用暴露成对外提供的服务,针对这个对外暴露的服务可以实现灰度发布、流量管理等。我们把这种流量管理称之为南北向流量管理,也就是入口请求到集群服务的流量管理。 而Istio是侧重于集群内服务之间的东西向流量管理、或者称之为服务网格之间的流量管理。 Istio是一个用于连接/管理以及安全化微服务的开放平台,提供了一种简单的方式用于创建微服务网络,并提供负载均衡、服务间认证以及监控等能力,并且关键的一点是并不需要修改服务本身就可以实现上述功能。 该样例应用由四个单独的微服务构成,用来演示多种 Istio 特性。该应用模仿某银行金融产品的一个分类,显示某一金融产品的信息。页面上会显示该产品的描述、明细,以及针对特定用户的增值服务。 四个单独的微服务: • productpage :productpage 微服务会调用 details 和 addedvalues两个微服务,用来生成页面。• details :该微服务包含了金融产品的信息。• addedvalues:该微服务包含了针对特定用户的增值服务。它还会调用 styletransfer微服务。• styletransfer:该微服务提供了转移照片艺术风格的API功能。 addedvalues微服务有 3 个版本: • v1 版本不会调用 styletransfer 服务,也不会提供风险和投资分析结果。• v2 版本不会调用 styletransfer 服务,但会提供风险和投资分析结果。• v3 版本会调用 styletransfer 服务,提供针对特定用户的增值服务,即允许用户上传图片进行风格转换,并返回一张转换后的图片。 准备Kubernetes集群 阿里云容器服务Kubernetes 1.10.4目前已经上线,可以通过容器服务管理控制台非常方便地快速创建 Kubernetes 集群。具体过程可以参考创建Kubernetes集群。 确保安装配置kubectl 能够连接上Kubernetes 集群。 示例中用到的文件请参考: 文件 部署Istio 打开容器服务控制台,在左侧导航栏中选中集群,右侧点击更多,在弹出的菜单中选中 部署Istio。 在打开的页面中可以看到Istio默认安装的命名空间、发布名称;通过勾选来确认是否安装相应的模块,默认是勾选前四项;第5项是提供基于日志服务的分布式跟踪能力,本演示中不启用。 点击 部署Istio 按钮,几十秒钟之后即可完成部署。 自动 Sidecar 注入 查看namespace: 点击编辑,为 default 命名空间打上标签 istio-injection=enabled。 使用 kubectl 部署简单的服务 kubectl apply -f app.yaml 上面的命令会启动全部的3个服务,其中也包括了 addedvalues 服务的三个版本(v1、v2 以及 v3) 定义 Ingress gateway kubectl apply -f gateway.yaml 确认所有的服务和 Pod 都已经正确的定义和启动 kubectl get svc kubectl get po 确认网关创建完成 kubectl get gateway 应用缺省目标规则 kubectl apply -f destination-rule-all.yaml 等待几秒钟,等待目标规则