ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

云原生是什么?从容器到微服务的核心技术栈与落地实践

云原生是什么?从容器到微服务的核心技术栈与落地实践 开头≥200字云原生到底是什么这个问题如果拿去问十个人可能会得到十一种答案。有人说是容器加微服务有人说是Kubernetes有人说DevOps也有人干脆说“云原生就是上云”。这些说法都对但又都不完整。作为一个在云端摸爬滚打了十几年的从业者我见过很多团队把“上云”当成“云原生”也见过不少团队把Kubernetes搞起来了就觉得自己已经“云原生”了结果业务该卡还是卡成本该超还是超。我自己第一次接触云原生这个概念是在2015年前后。那时候公司打算把一套传统Java应用搬到云上领导说“上云就完事了”结果真的只是把虚拟机从机房搬到了云厂商的控制台里。应用架构没变数据库没变运维方式也没变唯一变化的是账单从机房维护费变成了云资源租金。后来我才慢慢意识到云原生不是一个技术点而是一整套围绕“云”重新设计应用、开发、交付、运维的思维方式。它的核心不是“用到了云”而是“为云而生”。这篇文章我打算用最直白的方式把云原生这个被人为复杂化的概念拆开揉碎从它的来龙去脉讲到具体落地包括容器、编排、微服务、DevOps、可观测性这些关键词到底在解决什么问题以及一个普通团队到底该从哪一步开始切入。不堆概念只讲人话。1. 先搞清楚云原生到底在解决什么问题1.1 上云不等于云原生云原生是“为云而设计”很多人对云原生最大的误解就是把“部署在云上”当成“云原生”。我见过不少项目数据库跑在云厂商的托管实例上应用部署在云虚拟机上安全组、负载均衡都是云厂商的看起来好像什么都上云了但打开代码一看还是传统单体架构定时任务写在一台机器的crontab里session存在本地内存里日志打到本地文件扩展靠“把机器配置调高”。这种模式本质上是“把机房搬到了云上”云的弹性、按需付费、自动化运维这些核心优势根本用不上。云原生的本质是让应用从设计之初就假设自己运行在一个动态的、分布式的、随时可能变化的云环境里。环境里的计算资源不是固定的一台台服务器而是一池子随时可以申请和释放的算力。应用本身必须能适应这种动态性比如实例随时可能被销毁重建、网络随时可能发生变化、流量随时可能暴涨。只有在这样的前提下设计的应用才能真正吃到云的红利。打个比方传统上云就像是搬家把家里的家具原封不动搬到新房子。云原生则是你住进去之后发现新房子有智能家居系统于是重新规划了电路、水管、网络把家电全部换成智能设备让整个房子可以自动调节温度、光线和能耗。前者是“住进新房”后者是“为新房而设计”。1.2 云原生不是单一技术是一个技术栈组合云原生不是某一个具体的技术而是多个技术方向形成的一个组合拳。容器解决了打包和隔离的问题Kubernetes解决了容器大规模调度的问题微服务解决了应用拆分的问题DevOps解决了开发和运维协作的问题服务网格解决了微服务通信治理的问题可观测性解决了分布式系统排障的问题。这些技术单独拿出来都不是什么新鲜事物但把它们组合起来围绕云环境重新组织软件生命周期才构成了云原生的完整拼图。我用一张表格把这几个核心组件和它们解决的核心问题对应起来方便大家建立整体认知组件解决的核心问题类比容器应用打包、依赖隔离、环境一致标准化的集装箱Kubernetes容器的大规模调度、弹性伸缩、自愈港口的自动化调度系统微服务单体应用拆分、独立部署、故障隔离把一个公司拆成独立核算的小团队DevOps开发与运维协作、自动化交付让厨师和服务员一起设计菜单服务网格微服务间通信治理、流量控制、安全交通路网中的信号灯和交警可观测性日志、指标、链路追踪给系统装上仪表盘和行车记录仪这里需要强调一点云原生不是要求你一次性上齐所有这些技术。每个团队的阶段不同、业务复杂度不同完全可以渐进式推进。我后面会详细讲怎么一步步落地。1.3 云原生带来的核心价值弹性、速度、成本我接触过的团队在真正迈出云原生这一步之后感受到的变化主要集中在三个方面。第一个是弹性。传统架构应对流量峰值只能提前扩容机器而流量过去了机器就闲置了这部分钱等于白花。云原生架构下应用可以做到秒级甚至分钟级地根据流量自动伸缩高峰来了加机器高峰走了减机器账单跟着真实用量走。我做过一个电商项目大促期间的流量是平时的二十倍如果按峰值容量长期部署成本要高好几倍。上了Kubernetes加HPA之后大促期间自动扩容结束后自动缩容成本直接降了一半以上。第二个是速度。当应用被拆分成微服务每个服务都可以独立开发、独立测试、独立部署不同团队之间的发布不再互相牵制。配合DevOps流水线从代码提交到上线的时间可以从“几天一次”变成“一天几次”。这种交付速度的提升在业务竞争激烈的场景下是非常明显的优势。第三个是成本。云原生的成本优势不仅仅是省机器更重要的是省人。自动化运维、自愈能力、基础设施即代码这些手段可以把大量重复性的运维工作交给系统让团队里宝贵的工程师去做更有价值的事。2. 容器和Kubernetes云原生的地基2.1 容器为什么是云原生绕不开的基础如果你去参加任何一个云原生的技术会议几乎所有的演讲都会提到容器。为什么容器这么重要核心在于它解决了软件交付中的一个经典难题环境一致性。在没有容器的时代开发环境、测试环境、生产环境这三套环境往往会因为操作系统版本、依赖库版本、配置文件差异而出现“在我机器上能跑”的尴尬局面。开发说代码没问题测试说功能起不来运维说生产上出了bug三方互相扯皮最后往往是花大量时间在环境问题排查上。容器把应用连同它运行所需的代码、运行时、系统工具、库、配置全部打包在一起隔离成一个独立的运行单元。这个单元在任何安装了容器引擎的机器上表现都是一样的。本地能跑到了测试环境、生产环境也一定能跑。这就相当于集装箱运输的出现货物被标准化封装之后不管用哪种轮船、火车、卡车运输货物本身的状态都是一致的。在实际操作中我给大家一个建议应用镜像要做到不可变。也就是说一旦镜像构建完成就不允许任何人登录到容器里去改文件、装软件。任何修改都应该通过修改代码、重新构建镜像来完成。这个原则听起来简单但在实际团队里要真正做到需要配合一套自动化的构建流程以及团队纪律。我在下面“实操环节”会详细讲怎么做。2.2 Kubernetes到底在解决什么问题容器解决了单个应用的打包和运行问题但当你的应用有成百上千个容器实例时就需要一个东西来管理它们哪些节点上有空闲资源可以部署新容器哪个容器挂了需要重新拉起流量增加了需要扩容多少实例新版本要发布时怎么做到不中断服务。这些问题的答案就是Kubernetes。Kubernetes本质上是一个容器编排平台它把一组物理机或虚拟机抽象成一个统一的资源池然后在这个资源池上调度和运行容器。你可以告诉Kubernetes“我想要的最终状态是有3个副本的nginx在跑”Kubernetes就会想方设法让系统达到这个状态。某个副本挂了它会自动拉起一个新的节点宕机了它会把上面的容器迁移到其他节点流量大了它可以按配置自动增加副本数。用生活类比的话Kubernetes就像一个港口自动化调度系统。港口里停了无数艘船船上装了无数集装箱。调度系统知道每艘船的位置、剩余空间、卸货速度当一个新集装箱要进港时系统自动找一条最合适的船放上去。某艘船坏了系统自动把船上的集装箱转移到其他船。某个码头的货物吞吐量突然暴增系统自动调配更多集装箱和装卸设备过去。2.3 部署Kubernetes的三种主要方式新手怎么选对于初次接触Kubernetes的团队最大的困扰往往是“这玩意儿怎么搭”。市面上方案很多但归根结底就三类。第一类是使用云厂商的托管服务。这种方式最省心控制面由云厂商维护你只需要关心工作节点和业务应用。我的建议是如果团队没有特别强的Kubernetes运维能力优先用托管服务。开源Kubernetes的控制面组件虽然本身不复杂但高可用部署、证书轮换、etcd备份这些维护工作最终还是需要有人懂的人来做的。第二类是自己搭建Kubernetes集群。这适合对数据合规有严格要求、必须自建机房的团队。自己搭集群有很多工具生产环境推荐kubeadm加容器运行时的方式或者直接用发行版。自己搭的好处是自由度最高坏处是一切都要自己运维控制面高可用、网络插件、存储插件、监控告警全都要自己配置学习曲线非常陡。我见过不少团队为了省云厂商的托管费用去自建结果一个月光维护Kubernetes就用掉半个运维组的人力得不偿失。第三类是本地开发环境用轻量级方案。比如minikube、kind这些工具可以在本地电脑上跑一个单节点的Kubernetes集群主要用于开发调试和学习。新手入门我特别推荐先从这类方案开始把Kubernetes的基本概念玩明白再决定生产环境用什么方案。3. 微服务与DevOps应用架构与交付模式的变革3.1 从单体到微服务为什么要拆怎么拆才不后悔云原生的应用架构微服务是其中最常被提起、也最容易被误解的方向。很多团队一听说微服务好就急急忙忙把单体应用拆成几十个服务结果分布式事务、服务间调用链、运维复杂度这些问题一起涌上来最后项目延期、团队崩溃又急着把服务合并回去。这就是典型的“为拆而拆”。微服务的核心价值不在于“拆”而在于“独立”和“自治”。每个服务应该能独立开发、独立测试、独立部署、独立扩容最好是每个服务由一个小团队全权负责。如果一个服务拆出来之后发布还要跟着其他服务一起协调改一个接口要通知五个团队一起联调那这个拆分就是失败的。我整理了几个判断微服务拆分是否合理的标准大家可以拿来自检服务是否拥有独立的数据库或数据存储如果多个服务共享一个数据库表那它们本质上还是一家人。服务是否可以独立部署如果一个服务发布必须连带发布另一个服务说明边界划得不对。服务是否有明确的业务边界比如订单服务、支付服务、库存服务边界通常是围绕业务能力来划分的而不是围绕技术层次。服务团队是否足够支撑服务的运维一个服务拆出来之后从开发到监控到告警到排障都要有人负责。团队人数不够的时候建议保持单体或少量服务的形态。我自己见过最成功的微服务改造往往不是一步到位的。第一步先把模块边界理清楚第二步把单体应用改成模块化单体第三步再逐步把有独立演进需求的模块单独拆成服务。这个过程可能持续半年到一年但风险可控回退余地大。3.2 DevOps打破开发和运维之间的那堵墙传统模式下开发团队负责写代码写完往运维手里一扔运维负责部署和运维。开发不关心系统怎么跑运维不关心代码怎么写。这个模式的坏处很明显开发写的代码部署到生产环境出问题两边开始互相甩锅发布窗口要安排在深夜因为白天发布出问题影响业务。DevOps的核心是把开发和运维的职责、流程、工具链一体化。它不是让开发去做运维也不是让运维去写代码而是让整个交付过程自动化、可重复、可追踪。开发提交代码之后自动触发构建、测试、打包、部署每一步的状态都可视化出了问题能快速定位并回滚。落地DevOps的关键在于CI/CD流水线。我给大家一个参考的流水线阶段划分代码提交开发者将自己的代码分支合并到主干或者发布分支触发流水线。静态检查与单元测试自动跑代码规范检查、安全扫描、单元测试有问题就终止流水线。构建镜像将代码打包成容器镜像推送到镜像仓库。部署到测试环境自动部署到测试环境运行集成测试、冒烟测试。部署到预发环境人工确认或自动触发部署到预发环境进行最后的验收。部署到生产环境通过灰度发布或金丝雀发布的方式逐步放量到生产环境。这套流水线跑起来之后开发和运维的协作方式会发生质的变化。开发提交代码后可以通过流水线状态实时了解自己的改动是否通过测试、是否已经部署运维则从重复的部署操作中解放出来专注于监控、容量规划、应急响应这些更有深度的工作。3.3 容器化改造单体应用也可以上容器我知道有些人会担心“我的应用还是单体架构是不是就不能云原生了”其实不然。即便是传统单体应用也可以先容器化享受环境一致、快速部署、弹性伸缩的部分红利。单体应用容器化的基本步骤并不复杂。首先把应用的可运行产物和运行环境一起写进Dockerfile。其次把配置参数通过环境变量传入让镜像保持通用。然后构建镜像、推送到镜像仓库最后用容器运行。整个过程中业务代码甚至可以一行都不用改。我实际做过一个案例。一个老旧的Java单体应用之前部署在物理机上发布一次要停机半小时。容器化之后用滚动发布的方式可以实现不停止服务地平滑升级。每个新版本先启动一个新的容器实例等新实例就绪之后再逐步摘掉旧实例的流量发布过程完全没有中断。这对老系统的用户体验提升是非常直观的。从这个点开始再逐步引入Kubernetes、拆分微服务整个演进路径会顺畅很多。4. 服务网格、可观测性与云原生架构的另一面4.1 服务网格微服务越多越需要一台“交通管理器”微服务化之后服务之间要互相调用你必须考虑流量控制、超时、重试、熔断、鉴权、链路加密这些问题。最传统的做法是在每个服务的代码里集成一个SDK把这些问题都处理一遍。这在服务数量少的时候问题不大但服务数量多了之后SDK的版本升级、配置管理、语言限制就变成了一件很头疼的事。服务网格的核心思想是把服务间通信的治理能力从应用代码中剥离出来下沉到一个独立的代理层。每个服务的旁边部署一个轻量级代理所有进出服务的流量都经过这个代理。代理之间组成一个网状结构统一负责流量控制、超时重试、熔断、安全策略等。业务代码不再需要关心这些通信层面的逻辑只需要专注自己的业务。很多人初次接触服务网格会觉得它很酷但我要泼一点冷水如果你的微服务数量只有三五个或者你连服务间通信的稳定性问题都还没有遇到那服务网格的引入往往只会增加复杂度收益并不明显。我见过一些团队Kubernetes都没玩明白就急着上Istio结果配置复杂到没人能维护。服务网格是微服务规模发展到一定阶段之后才应该考虑的治理工具它是锦上添花不是雪中送炭。4.2 可观测性分布式系统排障的核心能力传统单体应用只有一个进程出了问题看日志、看监控就能八九不离十。微服务架构下一次用户请求可能要经过好几个服务每个服务都可能有副本、有负载均衡、有重试出了问题你很难知道这条请求到底走了哪条路径、卡在哪个环节。这时候就需要可观测性。可观测性包括三个支柱日志、指标、链路追踪。日志解决的是“发生了什么”的问题。所有服务把日志集中收集到一个统一的日志平台上比如ELK或Loki。排障的时候按关键词、按时间、按服务维度去检索。指标解决的是“系统状态怎么样”的问题。比如CPU使用率、内存使用率、请求QPS、错误率、响应时延。常用的采集方案是Prometheus加Grafana采集各服务和节点的指标数据在仪表盘上可视化展示。链路追踪解决的是“一次请求经过了哪些服务、每个环节耗时多少”的问题。这个能力在微服务架构下几乎是必不可少的。常用工具是Jaeger或SkyWalking。一条请求从入口开始生成一个唯一的追踪ID这个ID随请求在服务间传递每个服务记录自己的处理耗时。如果用户反馈某个操作很慢你在追踪系统里一搜就能看到是哪个服务拖慢了整体响应。4.3 云原生数据库与其他云服务别忽略背后的生态聊了这么多很多人可能忽略了一点云原生不仅是应用层的架构变革也包含数据层的演进。云原生数据库、对象存储、消息队列、Serverless函数计算这些云服务共同构成了云原生的整体生态。云原生数据库和传统数据库的核心区别在于架构设计和运维模式。传统数据库通常是单机或主从架构扩展能力有限运维需要DBA人工干预。云原生数据库则从设计上就是分布式、存算分离的架构存储和计算可以独立扩缩容高可用、备份、容灾这些能力由云厂商托管用户只需要通过API或控制台操作数据库资源可以随业务需求弹性伸缩。以我接触过的案例来说一个业务在促销季流量暴增时传统数据库需要提前准备规格足够高的主从实例用不到的时候也在持续付费。而云原生数据库可以根据负载自动扩容活动结束自动缩容成本更贴合实际使用。对于中小团队来说这省下来的不仅是钱更是一个全职DBA的人力成本。5. 落地实操一个小团队如何一步步走向云原生5.1 第一步现状盘点与目标设定前面讲了这么多概念和原理现在来说说最实际的一个普通团队到底该怎么落地云原生。我的建议是不要一口气上齐所有技术栈而是分阶段推进每个阶段都有明确的目标和可衡量的结果。第一阶段是固化环境。先把应用的运行环境容器化解决“在我机器上能跑”的问题。这一步的交付物是一套Dockerfile和容器运行规范目标是让任何一名团队成员拉下代码就能在本地跑起来。第二阶段是标准化交付。引入CI/CD流水线让构建、测试、部署从人工操作变成自动化流水线。这一步的交付物是一套可重复的发布流程目标是让发布从“高危操作”变成“日常操作”。第三阶段是提升弹性。将应用从虚拟机上迁移到Kubernetes集群配置好自动伸缩、自愈、滚动发布。这一步的交付物是Kubernetes部署清单和HPA配置目标是让系统具备基本的弹性能力。第四阶段是完善治理。根据业务复杂度决定是否引入微服务拆分、服务网格、混沌工程等更进阶的实践。这一步的前提是前三步已经稳定运转有足够的自动化测试和可观测性能力支撑。每进入下一个阶段之前我建议团队先复盘上一个阶段是否真正形成了稳定的习惯和收益。如果连容器化的环境一致性都没有感受到急着上Kubernetes只会增加复杂度。5.2 搭建一套最小可用的CI/CD流水线我分享一套最小可用的CI/CD流水线搭建思路以GitLab CI和Docker为例大家可以根据自己的技术栈替换成GitHub Actions、Jenkins等工具。假设我们有一个Python Web应用代码托管在GitLab上需要实现的流水线是提交代码后自动跑单元测试、构建镜像、推送到镜像仓库、部署到测试环境。在项目根目录下创建一个.gitlab-ci.yml文件内容大致如下stages: - test - build - deploy test: stage: test script: - pip install -r requirements.txt - pytest build: stage: build script: - docker build -t registry.example.com/myapp:latest . - docker push registry.example.com/myapp:latest deploy: stage: deploy script: - kubectl set image deployment/myapp myappregistry.example.com/myapp:latest这三段流水线分别对应三个stage。开发提交代码后第一个stage会安装依赖并运行单元测试测试挂了流水线就停在这里后面的构建和部署不会执行。测试通过后进入构建阶段把代码打包成容器镜像并推送到镜像仓库。最后进入部署阶段通过kubectl命令更新Kubernetes里deployment的镜像版本。这里有个细节值得说明部署阶段用的是kubectl set image这个命令会触发Kubernetes的滚动更新先启动新版本的Pod等新的Pod通过就绪检查后再逐个替换旧Pod。所以即使流水线自动化触发部署生产环境也不会出现全部实例同时重启的情况。5.3 常见问题与实战排障速查最后这部分我把团队在云原生落地过程中最常踩的坑和对应的排查思路整理成一个速查表希望能帮大家少走弯路。现象可能原因排查思路容器启动后立即退出应用进程异常退出或启动命令错误通过docker logs查看容器日志确认应用启动命令和参数是否正确Pod一直处于Pending状态节点资源不足或调度约束不满足用kubectl describe pod查看调度器给出的具体错误信息检查节点资源是否充足Pod反复重启处于CrashLoopBackOff应用启动时报错或探针配置错误查看容器日志和事件检查启动探针和就绪探针的配置是否合理服务偶尔超时时好时坏服务间调用超时设置过短或依赖的下游服务性能不稳定用链路追踪工具定位是哪个环节耗时增加适当调整超时和重试策略高并发下部分请求失败熔断未开启某个下游服务故障被无限放大检查服务间通信是否配置了熔断、限流、重试策略评估是否需要引入服务网格镜像构建非常慢依赖下载慢或构建缓存未生效使用镜像仓库的构建缓存功能把不常变的依赖层放在Dockerfile靠前的位置存储数据丢失容器或Pod被重建而未挂载持久化存储检查应用是否使用了持久卷声明把有状态服务的数据放到合适的存储中这些场景都是我在实际项目中真实遇到过的。云原生的学习曲线确实不陡峭它不像一门新语言学完语法就能写它更像一套工程方法论需要你在实践中不断理解每个组件存在的理由。我强烈建议每个准备入坑的团队先找一个最简单的服务完整地走一遍容器化、流水线、部署到Kubernetes的流程把整个链路打通再用同样的模式去推广到更多服务。云原生不是一个“做了就万事大吉”的方案它是一个持续演进的过程。很多团队在切到容器和Kubernetes之后以为事情结束了其实真正的挑战才刚刚开始如何让组织架构适应新的协作模式如何培养团队的可观测性意识如何在复杂度和收益之间找到平衡点。这些没有标准答案只取决于你所在团队的规模、业务形态和每个人的学习意愿。我个人在实际操作中的体会是云原生最大的价值不在于用了多少热门技术而在于它强制你重新思考应用的生命周期从开发、测试、发布到运行、监控、扩容每个环节都变成了可以自动化、可重复、可观测的工程实践。先把这条路走通再谈锦上添花。希望这篇文章能让你在云原生的学习路线上少一些迷茫多一些明确的方向感。
返回列表