从运维角度看微服务
微服务特点
- 服务组件化
每个服务独立开发、部署,有效避免一个服务的修改引起整个系统重新部署。
哪个服务出现问题,修改后重新部署,不影响整个业务。 - 技术栈灵活
约定通信方式,使得服务本身功能实现对技术要求不再那么敏感。
不再采用一种语言开发,通过对接接口互相访问。 - 独立部署
每个微服务独立部署,加快部署速度,方便扩展。 - 扩展性强
每个微服务可以部署多个,并且有负载均衡能力。 - 独立数据
每个微服务有独立的基本组件,例如数据库、缓存等。
微服务不足
- 沟通成本
- 数据一致性
- 运维成本
- 内部架构复杂性
- 大量服务如何治理
- 如何部署
- 如何监控
- 数据和事务
单体应用 vs 微服务
- 将一套业务 按功能拆分 每个服务处理自己的业务
- 每个业务都由独立的数据库
- 都通过 apigateway 网关 分发到独立的服务 ,路由转发
- 如何互相找到对方,通过注册中心,请求会去询问注册中心向哪里转发
单体架构优势:
- 易于部署
- 易于测试
单体架构不足:
- 代码膨胀,难以维护
- 构建、部署成本大
- 新人上手难
k8s治理微服务:
- 产品迭代
- 无状态
- 有状态很少应用
- 把每个微服务作为一个镜像去部署管理、迭代
SOA面向服务:
- 把每个功能做成api
- 每次增加功能服务都要让其他服务去感知
- 微服务可以通过 注册中心 感知
- SOA可能需要人工干预,细腻度比微服务大一些
Java 微服务框架
- Spring Boot
- Spring Cloud (Spring生态技术栈,Spring Boot的升级完善,应用最多)
- Dubbo (阿里云开源 )
在K8S平台部署微服务考虑的问题
微服务架构图
- 用户通过手机或者浏览器访问域名
- 访问域名到达前端(前后端分离),访问功能调用对应业务逻辑处理
- 访问功能的时候,通过负载均衡到达网关,网关作为微服务的入口,认证过滤降级都在这个层面实现,类似nginx的location
- 网关根据用户请求的url匹配,去注册中心去找提供相应的服务
- 注册中心保存了所有微服务的信息
- 每个微服务的数据都保存在自己的数据库中
- 配置中心存储每个微服务的配置数据,集中化管理
微服务架构的理解
- 微服务间如何通信?
- REST API,RPC,MQ
- 微服务如何发现彼此?
- 注册中心
- 组件之间怎么个调用关系?
- 查看商品 -> 订单 -> 库存 -> 支付
- 链路跟踪 打点 判断一个请求经过哪些环节
- 哪个服务作为整个网站入口?
- 网关 作为 服务端入口
- 前端 作为 用户访问入口
- 哪些微服务需要对外访问?
- 前端程序调用服务端接口api,前端和网关都需要暴露
- 微服务怎么部署?更新?扩容?
- java 应用 ,微服务jar包,更新jar包,再启动一个jar包服务
- 区分有状态应用与无状态应用
- 网关,微服务都是无状态的,包括注册中心
- 有状态的都进行持久化,数据库,redis,MQ,分布式存储都按照传统部署
为什么要用注册中心
微服务太多面临的问题:
- 怎么记录一个微服务多个副本接口地址? 新加入的服务如何让其他业务动态感知到
- 怎么实现一个微服务多个副本负载均衡?
- 怎么判断一个微服务副本是否可用? SOA的需要监控http,lv做健康检查
- 主流注册中心:Eureka,Nacos
1 | # 注册中心主要解决: 每个微服务之间如何交互 |
1 | # SOA |
Eureka注册中心
- 每启动一个微服务都会被注册到Eureka中
- Eureka不单存储信息,还提供负载均衡功能,检查接口是否可用
- 请求访问注册中心的服务,响应给请求的客户端,客户端再去请求微服务
- 客户端都集成在程序中
项目迁移到K8S平台是怎样的流程
制作镜像
- 镜像作为交付物
1 | 1. 基础镜像 CentOS |
控制器管理Pod
- Deployment:无状态部署
- StatefulSet:有状态部署
- DaemonSet:守护进程部署
- Job & CronJob:批处理
暴露应用
- 微服务之间都是通过注册中心通信,而不是走service,所以不大多数微服务不需要service
- 网关和前端需要ingress暴露
对外发布应用
Pod 数据持久化
容器部署过程中一般有以下三种数据:
- 启动时需要的初始数据,可以是配置文件 configmap,secret
- 启动过程中产生的临时数据,该临时数据需要多个容器间共享
- 启动过程中产生的持久化数据
日志与监控
主流方案:
- Filebeat+ELK
- Prometheus+Grafana
传统部署与K8S部署区别
在K8S平台部署 Spring Cloud 微服务项目
熟悉Spring Cloud微服务项目
1 | # 项目代码地址 |
- portal 域名 让用户访问
- portal 访问 ingress 暴露的域名
- 域名指向网关
代码编译构建
1 | # 将代码拉取到部署服务器 |
1 | [root@k8s-master1 simple-microservice-dev1]# pwd |
查看 gateway 配置文件
1 | [root@k8s-master1 resources]# cd /opt/microservic-code/simple-microservice-dev1/gateway-service/src/main/resources |
1 | # 不同环境区分 |
1 | [root@k8s-master1 resources]# cat application-fat.yml |
查看微服务的配置文件
1 | # 查看商品微服务的配置文件 |
查看 eureka 配置文件
1 | [root@k8s-master1 simple-microservice-dev1]# cd eureka-service/src/main/resources/ |
完成代码的编译构建
- 以上看的都是程序开发的源码,现在查看编译构建状态的代码 dev2
1 | # Maven项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的项目管理工具软件 |
- dev2中有构建脚本 dockerfile
1 | [root@k8s-master1 gateway-service]# cd /opt/microservic-code/simple-microservice-dev2/gateway-service |
1 | # 单独测试打包 和 整个项目打包 |
构建项目镜像并推送到镜像仓库
1 | # 当前是手动构建 使用脚本循环创建 |
部署Spring Cloud项目
部署环境要求
- CoreDNS
- Ingress Controller
- MySQL
1 | [root@k8s-master2 tomcat-java-demo]# yum -y install mariadb mariadb-server |
架构图
- eureka 有状态部署
- gateway portal 需要对外暴露
- 微服务通过注册中心,无需service
服务编排
- 每个微服务都需要yaml部署文件
- 别忘记更换镜像地址
gateway
1 | cd /opt/microservic-code/simple-microservice-dev3/k8s |
portal
1 | [root@k8s-master1 k8s]# cat portal.yaml |
eureka
1 | # StatefulSet |
1 | # 用在这里 |
order
1 | [root@k8s-master1 k8s]# cat order.yaml |
product
1 | [root@k8s-master1 k8s]# cat product.yaml |
stock
1 | [root@k8s-master1 k8s]# cat stock.yaml |
导入数据库文件到MySQL
1 | [root@k8s-master1 db]# scp *.sql root@172.17.70.252:/tmp |
修改配置文件
- 根据实际配置修改配置文件
- eureka的连裤串
- mysql连库串 大多在微服务上
- 如果有修改 需要重新执行脚本打包镜像,还要修改部署的yaml文件,手动很麻烦
- eureka的连库串 = StatefulSet 有状态部署
1 | cd /opt/microservic-code/simple-microservice-dev3/eureka-service/src/main/resources |
1 | # gateway |
1 | # portal |
1 | # order |
1 | # product |
1 | # stock |
1 | # 1. 有修改执行 重新生成镜像 |
部署资源
创建 命名空间
1 | [root@k8s-master1 k8s]# kubectl create ns ms |
创建镜像仓库资源凭证
1 | [root@k8s-master1 k8s]# kubectl create secret docker-registry registry-pull-secret --docker-server=172.17.70.252 --docker-username=admin --docker-password=lx@68328153 --docker-email=admin@ctnrs.com -n ms |
部署 eureka
1 | # 有状态应用部署 会一个一个启动 |
1 | # 访问管理页面 |
部署 gateway
1 | # 资源不太够 我就部署一个副本 |
部署 portal
1 | [root@k8s-master1 k8s]# kubectl apply -f portal.yaml |
部署所有微服务
1 | # 如果测试的资源不足 可以用1个副本 |
部署的服务 都被注册中心发现
测试访问 portal
1 | # 加入hosts |
程序调用gateway域名的位置
1 | # 都是前端程序调用 |
在K8S中部署Eureka集群
- 相互注册
- 提供健康检查,podIP检查,如果不正常就不会将客户端的请求发送给它
- 剔除一个eureka,集群也可正常工作
1 | # 如果直接对接k8s |
生产环境踩坑经验分享
限制了容器资源,还经常被杀死
1 | env: |
1 | 1. k8s部署java应用 ,docker不能自动发现java设置的内存,jvm限制无用 |
滚动更新之健康检查重要性
- 误判健康检查的失败,需要根据应用启动时间判断
- java启动时间较慢 最好设置一分钟或以上
- readinessProbe 就绪检查 它检查的是Ready 1/1 准备就绪才会分配流量,否则不会被访问到。微服务不受service控制,所以加不加无所谓
- livenessProbe 存活检查 判断pod中的应用是否启动成功,比如判断服务端口,如果不成功会根据拉起策略重建,直到启动成功为止
- 具体要看java服务的启动时间来设置
- periodSeconds 周期,每间隔10秒来判断一次,可以设置较长
1 | readinessProbe: |
滚动更新之流量丢失
- 运行的服务pod 已经被注册到 注册中心里了
- 如果滚动更新 已分配的流量怎么办
- 只能为每个pod 增加退出时间,让pod等待退出时间 再去销毁
- 滚动更新是突然kill 没有更新提示,kill之后根据退出时间等待销毁
- 优雅退出
1 | https://mp.weixin.qq.com/s?__biz=MzAwNTM5Njk3Mw==&mid=2247487699&idx=1&sn=7bf9c62b02e017de5666ab4ade9ef3d1&chksm=9b1c1051ac6b99476bf665bf1e10ac3aca9b0b995c2da944909621660547f2dd382845302aba&mpshare=1&scene=23&srcid=&sharer_sharetime=1577617482898&sharer_shareid=e3aed5c2fdec06d648a99b41c36fe174#rd |
配置中心 和 注册中心 和 数据库
1 | 1. 配置中心 apollo 安装服务端,配置文件在里面配置好 |
1 | 1. eureka 可以部署在pod中,也可以部署在pod之外,只要网络互通即可 |
1 | 1. 还是建议微服务中的数据存储使用原先的数据库存储,而不是使用有状态部署 |
增加pod流程
- 新增的pod会先加入 eureka里
- 如果有客户端去请求 eureka 查询这个pod所在的后面业务功能IP,eureka健康检查通过新的pod的后就可以响应回去
- gateway就作为请求的客户端,根据url查询对应的pod,eureka查询出对应的ip根据负载均衡策略给gateway,gateway再去响应。