Kubernetes,微服务以及 Service Mesh

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 这是前一段时间在一个微服务的 meetup 上的分享,整理成文章发布出来。 谈微服务之前,先澄清一下概念。微服务这个词的准确定义很难,不同的人有不同的人的看法。比如一个朋友是『微服务原教旨主义者』,坚持微服务一定是无状态的 http API 服务,其他的都是『邪魔歪道』,它和 SOA,RPC,分布式系统之间有明显的分界。

这是前一段时间在一个微服务的 meetup 上的分享,整理成文章发布出来。


谈微服务之前,先澄清一下概念。微服务这个词的准确定义很难,不同的人有不同的人的看法。比如一个朋友是『微服务原教旨主义者』,坚持微服务一定是无状态的 http API 服务,其他的都是『邪魔歪道』,它和 SOA,RPC,分布式系统之间有明显的分界。而另外也有人认为微服务本身就要求把整体系统当做一个完整的分布式应用来对待,而不是原来那种把各种组件堆积在一起,『拼接』系统的做法。两种说法都有道理,因为如果微服务没有个明确的边界的话,你可能会发现微服务囊括了一切,但如果只是坚持无状态,那微服务相关的一些领域又无法涵盖。我个人对这个问题持开放式的看法,微服务本身代表了一种软件交付以及复用模式的变化,从依赖库到依赖服务,和 SOA 有相通之处,同时它也带来了新的挑战,对研发运维都有影响,所有因为和微服务相关而产生的变化,都可以囊括在大微服务主题下。

微服务带来的变化

微服务主要给我们带来的变化有三点,

  1. 部署单元 越来越小的粒度,加快交付效率,同时增加运维的复杂度。
  2. 依赖方式 从依赖库到依赖服务,增加了开发者选择的自由(语言,框架,库),提高了复用效率,同时增加了治理的复杂度。
  3. 架构模式 从单体应用到微服务架构,架构设计的关注点从分层转向了服务拆分。

##微服务涉及的技术点

  • 服务注册与发现 服务目录 服务列表 配置中心
  • 进程间通讯 负载均衡
  • 服务生命周期管理 部署, 变更,升级,自动化运维
  • 服务依赖关系
  • 链路跟踪,限流,降级,熔断
  • 访问控制
  • 日志与监控
  • 微服务应用框架

这个是我浏览了众多微服务的话题之后摘要出来的一些技术点,不全面也不权威,不过可以看出,微服务主题涉及的面非常广,那这些问题在微服务之前就不存在么?为什么大家谈到微服务的时候才把这些东西拿出来说。这个需要从软件开发的历史说来。软件开发行业从十多年前开始,出现了一个分流,一部分是企业应用开发,软件要安装到企业客户自己的资源上,客户负责运维(或者通过技术支持),一部分是互联网软件,自己开发运维一套软件通过网络给最终用户使用。这两个领域使用的技术栈也逐渐分化,前者主要关注标准化,框架化(复用),易安装,易运维,后者主要关注高可用,高性能,纵向伸缩。而这两个领域到微服务时代,形成了一个合流,都在搞微服务化。主要原因我认为有两点:

  1. SaaS 的兴起,使得一些企业应用厂商也开始采用互联网模式,遇到用户规模的问题。同时企业应用很难纯 SaaS 化,面对大客户的时候,势必面临私有部署的问题,所以必须探索一种既能支撑用户规模,同时要能方便私有化部署的架构。
  2. 随着互联网技术领域为了应对新的业务变化,需要将原来的技术经验沉淀继承过来,开始关注标准化和框架化。

也就是说,这些技术点并不是新问题,但如何将这些技术点从业务逻辑中抽取出来,作为独立的,可复用的框架或者服务,这个是大家探寻的新问题。

为微服务而生的 Kubernetes

  • Kubernetes Pod – Sidecar 模式
  • Kubernetes 支持微服务的一些特性
  • Service Mesh 微服务的中间件层

我这样说,不仅仅是因为这是一个微服务的 meetup,微服务和 Kubernetes 确实是相辅相成的。一方面 Kubernetes 帮助微服务落地,另外一方面微服务促进了对容器和 Kubernetes 的需求。

Kubernetes 的 Pod – Sidecar 模式

我们先从 Kubernetes 的 Pod 机制带来的一种架构模式 — Sidecar 来说。下面这段配置文件是我从 Kubernetes 内置的 dns 的 deployment 中抽取出来的 Pod 描述文件,简化掉了资源限制,端口设置以及健康检查等内容。

apiVersion: v1
kind: Pod
metadata:
 name: dnspod
spec:
 containers: - name: kubedns
 image: gcr.io/google_containers/k8s-dns-kube-dns-amd64:1.14.4
 args: - --domain=cluster.local. - --dns-port=10053 - --config-dir=/kube-dns-config
 - --v=2
 ports:
 - containerPort: 10053
 name: dns-local
 protocol: UDP
 - containerPort: 10053
 name: dns-tcp-local
 protocol: TCP
 - name: dnsmasq
 image: gcr.io/google_containers/k8s-dns-dnsmasq-nanny-amd64:1.14.4
 args: - -v=2 - -logtostderr
 - -configDir=/etc/k8s/dns/dnsmasq-nanny
 - -restartDnsmasq=true - -- - -k
 - --cache-size=1000 - --log-facility=- - --server=/cluster.local/127.0.0.1#10053 - --server=/in-addr.arpa/127.0.0.1#10053 - --server=/ip6.arpa/127.0.0.1#10053
 ports: - containerPort: 53
 name: dns
 protocol: UDP
 - containerPort: 53
 name: dns-tcp
 protocol: TCP
 - name: sidecar
 image: gcr.io/google_containers/k8s-dns-sidecar-amd64:1.14.4
 args: - --v=2 - --logtostderr
 - --probe=kubedns,127.0.0.1:10053,kubernetes.default.svc.cluster.local,5,A
 - --probe=dnsmasq,127.0.0.1:53,kubernetes.default.svc.cluster.local,5,A

从这个配置文件可以看出,Kubernetes 的 Pod 里可以包含多个容器,同一个 Pod 的多个容器之间共享网络,Volume,IPC(需要设置),生命周期和 Pod 保持一致。上例中的 kube-dns 的作用是通过 Kubernetes 的 API 监听集群中的 Service 和 Endpoint 的变化,生成 dns 记录,通过 dns 协议暴露出来。dnsmasq 的作用是作为 dns 缓存,cluster 内部域名解析代理到 kube-dns,其他域名通过上游 dns server 解析然后缓存,供集群中的应用使用。sidecar 容器的作用是进行 dns 解析探测,然后输出监控数据。

通过这个例子可以看出来,Kubernetes 的 Pod 机制给我们提供了一种能力,就是将一个本来要捆绑在一起的服务,拆成多个,分为主容器和副容器(sidecar),是一种更细粒度的服务拆分能力。当然,没有 Kubernetes 的时候你也可以这么干,但如果你的程序需要几个进程捆绑在一起,要一起部署迁移,运维肯定想来打你。有了这种能力,我们就可以用一种非侵入的方式来扩展我们的服务能力,并且几乎没有增加运维复杂度。这个在我们后面的例子中也可以看到。

Kubernetes 上的服务发现

服务发现其实包含了两个方面的内容,一种是要发现应用依赖的服务,这个 Kubernetes 提供了内置的 dns 机制和 ClusterIP 机制,每个 Service 都自动注册域名,分配 ClusterIP,这样服务间的依赖可以从 IP 变为 name,这样可以实现不同环境下的配置的一致性。

apiVersion: v1
kind: Service
metadata:
 name: myservice
spec:
 ports: - port: 80
 protocol: TCP
 targetPort: 9376
 selector:
 app: my-app
 clusterIP: 10.96.0.11
curl http://myservice

如上面的 Service 的例子,依赖方只需要通过 myservice 这个名字调用,具体这个 Service 后面的后端实例在哪,有多少个,用户不用关心,交由 Kubernetes 接管,相当于一种基于虚 IP(通过 iptables 实现) 的内部负载均衡器。(具体的 clusterIP 实现这里不再详述,可查阅相关资料)。

另外一种服务发现是需要知道同一个服务的其他容器实例,节点之间需要互相连接,比如一些分布式应用。这种需求可以通过 Kubernetes 的 API 来实现,比如以下实例代码:

endpoints, _ = client.Core().Endpoints(namespace).Get("myservice", metav1.GetOptions{})
addrs := []string{} for _, ss := range endpoints { for _, addr := range ss.Addresses {
		ips = append(addrs, fmt.Sprintf(`"%s"`, addr.IP)) } }
glog.Infof("Endpoints = %s", addrs)

Kubernetes 提供了 ServiceAccount 的机制,自动在容器中注入调用 Kubernetes API 需要的 token,应用代码中无需关心认证问题,只需要部署的时候在 yaml 中配置好合适的 ServiceAccount 即可。

关于服务发现再多说两句,没有 Kubernetes 这样的统一平台之前,大家做服务发现还主要依赖一些服务发现开源工具,比如 etcd,zookeeper,consul 等,进行自定义开发注册规范,在应用中通过 sdk 自己注册。但当应用部署到 Kubernetes 这样的平台上你会发现,应用完全不需要自己注册,Kubernetes 本身最清楚应用的节点状态,有需要直接通过 Kubernetes 进行查询即可,这样可以降低应用的开发运维成本。

通过 Kubernetes 进行 Leader 选举

有些分布式需要 Leader 选举,在之前,大家一般可能依赖 etcd 或者 zookeeper 这样的服务来实现。如果应用部署在 Kubernetes 中,也可以利用 Kubernetes 来实现选举,这样可以减少依赖服务。

Kubernetes 中实现 Leader 选举主要依赖以下特性:

  1. Kubernetes 中的所有 API 对象类型,都有一个 ResourceVersion,每次变更,版本号都会增加。
  2. Kubernetes 中的所有对象,都支持 Annotation,支持通过 API 修改,这样可以附加一些自定义的 key-value 数据保存到 Kubernetes 中。
  3. Kubernetes 的所有 API 对象中,Endpoint/ConfigMap 属于『无副作用』对象,也就是说,创建后不会带来额外的影响,所以一般用这两种对象来保存选主信息。
  4. Kubernetes 的 Update/Replace 接口,支持 CAS 机制的更新,也就是说,更新时可以带上客户端缓存中的 ResourceVersion,如果服务器端该对象的 ResourceVersion 已经大于客户端传递的 ResourceVersion,则会更新失败。

这样,所有的节点都一起竞争更新同一个 Endpoint/ConfigMap,更新成功的,作为 Leader,然后把 Leader 信息写到 Annotation 中,其他节点也能获取到。为了避免竞争过于激烈,会有一个过期机制,过期时间也写入到 Annotation,Leader 定时 renew 过期时间,其他节点定时查询,发现过期就发起新一轮的竞争。这样相当于 Kubernetes 提供了一种以 client 和 API 一起配合实现的 Leader 选举机制,并且在 client sdk 中提供。当前 Kubernetes 的一些内部组件,比如 controller-manager,也是通过这种方式来实现选举的。

当然,如果觉得调用 client 麻烦,或者该语言的 sdk 尚未支持这个特性(应该只有 go 支持了),可以也可以通过 sidercar 的方式实现,参看 https://github.com/kubernetes/contrib/tree/master/election 。也就是说,通过一个 sidecar 程序去做选主,主程序只需要调用 sidecar 的一个 http api 去查询即可。

$ kubectl exec elector-sidecar -c nodejs -- wget -qO- http://localhost:8080 Master is elector-sidecar

Kubernetes 支持微服务的运维特性

Kubernetes 对微服务的运维特性上的支持,主要体现以下两方面:

  1. 滚动升级以及自动化伸缩
    kubectl set image deployment <deployment> <container>=<image>
    kubectl rollout status deployment <deployment>
    kubectl rollout pause deployment <deployment>
    kubectl rollout resume deployment <deployment>
    kubectl rollout undo deployment <deployment>
    kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10

    滚动升级支持最大不可用节点设置,支持暂停,恢复,一键回滚。自动伸缩支持根据监控数据在一个范围内自动伸缩,很大程度上降低了微服务的运维成本。

  2. 日志与监控的标准化通过 Kubernetes 可以实现日志收集以及应用监控的标准化,自动化,这样应用不用关心日志和监控数据的收集展示,只需要按照系统标准输出日志和监控接口即可。

Service Mesh 微服务的中间件层

微服务这个话题虽然火了有很长时间了,关于微服务的各种框架也有一些。但这些框架大多是编程语言层面来解决的,需要用户的业务代码中集成框架的类库,语言的选择也受限。这种方案很难作为单独的产品或者服务给用户使用,升级更新也受限于应用本身的更新与迭代。直到 Service Mesh 的概念的提出。Service Mesh 貌似也没有比较契合的翻译(有的译做服务齿合层,有的翻译做服务网格),这个概念就是试图在网络层抽象出一层,来统一接管一些微服务治理的功能。这样就可以做到跨语言,无侵入,独立升级。其中前一段时间 Google,IBM,Lyft 联合开源的 istio 就是这样一个工具,先看下它的功能简介:

  • 智能路由以及负载均衡
  • 跨语言以及平台
  • 全范围(Fleet-wide)策略执行
  • 深度监控和报告

是不是听上去就很厉害?有的还搞不明白是啥意思?我们看 istio 之前先看看 Service Mesh 能在网络层做些什么。

  • 可视化 其实本质上微服务治理的许多技术点都包含可视化要求,比如监控和链路追踪,比如服务依赖
  • 弹性(Resiliency 或者应该叫柔性,因为弹性很容易想到 scale) 就是网络层可以不那么生硬,比如超时控制,重试策略,错误注入,熔断,延迟注入都属于这个范围。
  • 效率(Efficiency) 网络层可以帮应用层多做一些事情,提升效率。比如卸载 TLS,协议转换兼容
  • 流量控制 比如根据一定规则分发流量到不同的 Service 后端,但对调用方来说是透明的。
  • 安全保护 在网络层对流量加密/解密,增加安全认证机制,而对应用透明。

可以看出,如果接管了应用的进出流量,将网络功能可编程化,实际上可做的事情很多。那我们简单看下 istio 是如何做的。

大致看一下这个架构图,istio 在业务 Pod 里部署了一个 sidecar — Envoy,这是一个代理服务器,前面说的网络层功能基本靠它来实现,然后 Envoy 和上面的控制层组件(Mixer,Pilot,Istio-Auth)交互,实现动态配置,策略执行,安全证书获取等,对用户业务透明。实际部署的时候,并不需要开发者在自己的 Pod 声明文件里配置 Envoy 这个 sidecar,istio 提供了一个命令行工具,在部署前注入(解析声明配置文件然后自动修改)即可,这样 istio 的组件就可以和业务应用完全解耦,进行独立升级。

看到这里,Java 服务器端的研发人员可能会感觉到,这个思路和 Java 当初的 AOP(aspect-oriented programming) 有点像,确实很多 Java 微服务框架也是利用 AOP 的能力来尽量减少对应用代码的侵入。但程序语言的 AOP 能力受语言限制,有的语言里就非常难实现非侵入的 AOP,而如果直接在网络层面寻找切面就可以做到跨语言了。

当前其实已经有许多在网络层实现的中间件,比如有提供数据库安全审计功能的,有提供 APM 的,有提供数据库自动缓存的,但这些中间件遇到的最大问题是增加了用户应用的部署复杂度,实施成本比较高,很难做到对用户应用透明。可以预见,随着 Kubernetes 的普及,这类中间件也会涌现出来。

结语

本质上,微服务的目的是想以一种架构模式,应对软件所服务的用户的规模增长。没有微服务架构之前,大多数应用是以单体模式出现的,只有当规模增长到一定程度,单体架构满足不了伸缩的需求的时候,才考虑拆分。而微服务的目标是在一开始的时候就按照这种架构实现,是一种面向未来的架构,也就是说用开始的选择成本降低以后的重构成本。用经济学的观点来说,微服务是技术投资,单体应用是技术债务,技术有余力那可以投资以期待未来收益,没余力那就只能借债支持当前业务,等待未来还债。而随着微服务基础设施的越来越完善,用很小的投资就可以获得未来很大的收益,就没有理由拒绝微服务了。

本文转自中文社区-Kubernetes,微服务以及 Service Mesh

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
相关文章
|
28天前
|
存储 运维 Kubernetes
容器服务ACK常见问题之修改service的名字失败如何解决
容器服务ACK(阿里云容器服务 Kubernetes 版)是阿里云提供的一种托管式Kubernetes服务,帮助用户轻松使用Kubernetes进行应用部署、管理和扩展。本汇总收集了容器服务ACK使用中的常见问题及答案,包括集群管理、应用部署、服务访问、网络配置、存储使用、安全保障等方面,旨在帮助用户快速解决使用过程中遇到的难题,提升容器管理和运维效率。
|
5天前
|
Kubernetes 监控 Cloud Native
构建高效云原生应用:基于Kubernetes的微服务治理实践
【4月更文挑战第13天】 在当今数字化转型的浪潮中,企业纷纷将目光投向了云原生技术以支持其业务敏捷性和可扩展性。本文深入探讨了利用Kubernetes作为容器编排平台,实现微服务架构的有效治理,旨在为开发者和运维团队提供一套优化策略,以确保云原生应用的高性能和稳定性。通过分析微服务设计原则、Kubernetes的核心组件以及实际案例,本文揭示了在多变的业务需求下,如何确保系统的高可用性、弹性和安全性。
11 4
|
25天前
|
Kubernetes Nacos 微服务
nacos常见问题之v2.2.3 k8s 微服务注册nacos强制删除 pod不消失如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
22 1
nacos常见问题之v2.2.3 k8s 微服务注册nacos强制删除 pod不消失如何解决
|
28天前
|
Kubernetes SDN 微服务
微服务与 Kubernetes 容器云的边界
【2月更文挑战第30天】该文探讨了微服务与Kubernetes集群的关系,主要关注是否应跨多集群部署。理想的状况是每个微服务对应一个Kubernetes集群,配置和注册中心在同一集群内,以减少网络延迟。
|
29天前
|
Kubernetes 开发者 Docker
构建高效微服务架构:Docker与Kubernetes的完美搭档
【2月更文挑战第29天】在当今快速发展的软件开发领域,微服务架构已成为提高系统可维护性、扩展性和敏捷性的关键解决方案。本文将深入探讨如何利用Docker容器化技术和Kubernetes集群管理工具,共同构建一个既高效又可靠的微服务环境。我们将分析Docker和Kubernetes的核心功能,并展示它们如何协同工作以简化部署流程、增强服务发现机制以及实现无缝的服务伸缩。通过实际案例分析,本文旨在为开发者提供一套实用的微服务架构设计和实施指南。
|
2月前
|
开发者 Docker 微服务
深入浅出:使用Docker容器化部署微服务架构
在当今快速迭代的软件开发环境中,微服务架构因其高度解耦和独立性而成为企业首选。然而,微服务的管理和部署可能会变得复杂和繁琐。本文将探讨如何利用Docker,一个轻量级的容器化技术,来简化和加速微服务的部署。我们将从Docker的基础概念入手,详细介绍如何创建、配置和运行微服务容器,最后讨论Docker在微服务架构中的优势和挑战。本文旨在为开发者提供一条清晰的路径,通过容器化技术实现微服务架构的高效部署和管理。
86 0
|
2月前
|
Kubernetes 开发者 Docker
深入浅出:使用Docker容器化部署微服务架构
在当今快速演进的软件开发领域,微服务架构因其高度的模块化和可伸缩性而受到广泛欢迎。然而,微服务的部署和管理也带来了新的挑战。本文旨在通过深入浅出的方式,探讨如何利用Docker容器技术有效地部署和管理微服务架构。我们将从Docker的基本概念出发,逐步深入到如何构建、部署微服务,并讨论在此过程中可能遇到的常见问题及其解决策略。本文不仅适合刚接触Docker和微服务的新手,也为有经验的开发者提供了实用的参考。
55 1
|
2月前
|
JSON JavaScript Docker
深入浅出:使用Docker容器化部署微服务架构
本文旨在向读者展示如何利用Docker技术高效地构建和部署微服务架构。通过深入浅出的方式,我们将探索Docker的基本概念、容器化的优势以及如何将其应用于微服务架构中。此外,文章还将提供一个简单的示例,指导读者实践如何使用Docker将一个现有的后端应用容器化,并部署到本地开发环境中。不同于传统的摘要,这里我们强调实践操作的重要性,鼓励读者通过实际操作来加深对Docker和微服务架构的理解。
52 1
|
2月前
|
Kubernetes 负载均衡 Docker
深入浅出:使用Docker容器化部署微服务
在当今快速变化的软件开发领域,微服务架构因其高度的模块化和可伸缩性而受到广泛欢迎。然而,微服务的部署和管理带来了新的挑战。本文将探讨如何利用Docker容器技术,简化和加速微服务应用的部署过程。我们将从Docker的基础知识入手,逐步深入到如何构建、部署和管理微服务容器。通过本文,读者将获得一套实用的工具和方法论,以便在自己的项目中高效地应用Docker和微服务技术。
|
2月前
|
Java 开发者 Docker
深入浅出:使用Docker容器化部署微服务架构
在本文中,我们将探索Docker容器技术如何革新微服务架构的部署方式,提高开发效率和应用的可扩展性。不同于传统摘要的概述风格,我们将通过一个实际案例,步骤明晰地展示如何将一个简单的微服务应用容器化,并在Docker环境中部署运行。本文旨在为开发者提供一个清晰、易懂的指南,帮助他们理解容器化技术的基本原理和操作流程,无论是初学者还是有经验的开发人员都能从中获益。

推荐镜像

更多