Istio思考往前一小步~系列一
思考起源于现实应用需求,随着微服务理念普及,基础设施从单机到容器到Kubernetes,体验过集群的各种好处之后,我们还缺少什么?为什么还要在kubernetes的基础上部署Istio?个人认为Istio提供了更安全、精细的流量治理能力,更便捷的对内/外访问方式,更好的多集群融合能力等;
我理解的四个不同的层次
1、最基础的是使用集群的VirtualService/DestinationRule/Gateway实现基础的流量管控,使用ServiceEntry/WorkloadEntry做外部服务管理https://www.cnblogs.com/it-worker365/p/17030958.html
2、对于多集群的公司,利用Istio做多集群管控 https://istio.io/v1.9/zh/docs/setup/install/multicluster/
3、对于透明代理功能无法满足的场景,比如rediscluster模式跨集群访问问题、比如需要动态修改配置生效的问题等...需要对透明代理功能升级利用EnvoyFilter配置或二开
4、proxyless,自定义解析执行xds协议,提升性能,增加多场景协议灵活性
5、Envoy性能优化及功能二开
其中1和2是先有成熟方案,3是今天想展开讨论的,4后续文章再议
结合图理解几个概念,对于istio,连接envoy发送请求的叫下游,envoy与外部建立连接发送请求的服务叫上游;
请求经过listener(envoy监听端口,等待外部连接)->route(路由)->cluster(负载到Endpoint)->endpoint(均衡到后端服务)
这个流程是如何交互的,首先通过看istio代码结合系统部署,可以看到,istiod启动是启动了pilot-discovery(控制面逻辑,监听k8s资源、监听CRD,下发xds指令到envoy)
而服务边车启动的是pilot-agent 生成envoy配置、管理envoy生命周期、监听管理证书更新等 & envoy进程(流量接管)
业务服务容器角度来看有两个,一个是Terminated状态,另一个是运行中的包含业务和边车的容器
istio-init负责初始化iptables规则,以便可以开启拦截,源码在cni这一部分
同样使用了到了tools包中的iptables支持工具,规则不了解的可以参考https://www.cnblogs.com/it-worker365/p/17031120.html
最终执行命令
istiod启动
通过pilot-discovery的main.go方法启动调用cmd.go
cmd.go中初始化且启动bootstrap
保留关键信息,Server.go初始化封装了安全认证、监控以及XDSServer的初始化启动
func NewServer(args *PilotArgs, initFuncs ...func(*Server)) (*Server, error) {
......
s := &Server{
clusterID: getClusterID(args),
environment: e,
fileWatcher: filewatcher.NewWatcher(),
httpMux: http.NewServeMux(),
monitoringMux: http.NewServeMux(),
readinessProbes: make(map[string]readinessProbe),
workloadTrustBundle: tb.NewTrustBundle(nil),
server: server.New(),
shutdownDuration: args.ShutdownDuration,
internalStop: make(chan struct{}),
istiodCertBundleWatcher: keycertbundle.NewWatcher(),
}
......
s.XDSServer = xds.NewDiscoveryServer(e, args.PodName, args.RegistryOptions.KubeOptions.ClusterAliases)
s.XDSServer.InitGenerators(e, args.Namespace, s.internalDebugMux)// Secure gRPC Server must be initialized after CA is created as may use a Citadel generated cert.
if err := s.initSecureDiscoveryService(args); err != nil {
return nil, fmt.Errorf("error initializing secure gRPC Listener: %v", err)
}// This should be called only after controllers are initialized.
s.initRegistryEventHandlers()
s.initDiscoveryService()
s.initSDSServer()
......
return s, nil
}
注册Handler,最后都是XDSServer.ConfigUpdate(pushReq)
// initRegistryEventHandlers sets up event handlers for config and service updates
func (s *Server) initRegistryEventHandlers() {
log.Info("initializing registry event handlers")
// Flush cached discovery responses whenever services configuration change.
serviceHandler := func(svc *model.Service, _ model.Event) {
pushReq := &model.PushRequest{
Full: true,
ConfigsUpdated: sets.New(model.ConfigKey{Kind: kind.ServiceEntry, Name: string(svc.Hostname), Namespace: svc.Attributes.Namespace}),
Reason: []model.TriggerReason{model.ServiceUpdate},
}
s.XDSServer.ConfigUpdate(pushReq)
}
s.ServiceController().AppendServiceHandler(serviceHandler) if s.configController != nil {
configHandler := func(prev config.Config, curr config.Config, event model.Event) {
defer func() {
if event != model.EventDelete {
s.statusReporter.AddInProgressResource(curr)
} else {
s.statusReporter.DeleteInProgressResource(curr)
}
}()
log.Debugf("Handle event %s for configuration %s", event, curr.Key())
// For update events, trigger push only if spec has changed.
if event == model.EventUpdate && !needsPush(prev, curr) {
log.Debugf("skipping push for %s as spec has not changed", prev.Key())
return
}
pushReq := &model.PushRequest{
Full: true,
ConfigsUpdated: sets.New(model.ConfigKey{Kind: kind.FromGvk(curr.GroupVersionKind), Name: curr.Name, Namespace: curr.Namespace}),
Reason: []model.TriggerReason{model.ConfigUpdate},
}
s.XDSServer.ConfigUpdate(pushReq)
}
schemas := collections.Pilot.All()
if features.EnableGatewayAPI {
schemas = collections.PilotGatewayAPI.All()
}
for _, schema := range schemas {
// This resource type was handled in external/servicediscovery.go, no need to rehandle here.
if schema.Resource().GroupVersionKind() == collections.IstioNetworkingV1Alpha3Serviceentries.
Resource().GroupVersionKind() {
continue
}
if schema.Resource().GroupVersionKind() == collections.IstioNetworkingV1Alpha3Workloadentries.
Resource().GroupVersionKind() {
continue
}
if schema.Resource().GroupVersionKind() == collections.IstioNetworkingV1Alpha3Workloadgroups.
Resource().GroupVersionKind() {
continue
} s.configController.RegisterEventHandler(schema.Resource().GroupVersionKind(), configHandler)
}
if s.environment.GatewayAPIController != nil {
s.environment.GatewayAPIController.RegisterEventHandler(gvk.Namespace, func(config.Config, config.Config, model.Event) {
s.XDSServer.ConfigUpdate(&model.PushRequest{
Full: true,
Reason: []model.TriggerReason{model.NamespaceUpdate},
})
})
}
}
}
ConfigUpdate做的事情是统计监控,将请求放入pushChannel
DiscoveryServer是envoy xds协议在pilot处理的grpc实现,截取类型中比较重要的几个对象,所需模型、数据生成器、防抖队列,xds推送缓冲队列以及与envoy数据面的连接集合
// DiscoveryServer is Pilot's gRPC implementation for Envoy's xds APIs
type DiscoveryServer struct {
// Env is the model environment.
Env *model.Environment // ConfigGenerator is responsible for generating data plane configuration using Istio networking
// APIs and service registry info
ConfigGenerator core.ConfigGenerator
// pushChannel is the buffer used for debouncing.
// after debouncing the pushRequest will be sent to pushQueue
pushChannel chan *model.PushRequest // pushQueue is the buffer that used after debounce and before the real xds push.
pushQueue *PushQueue // adsClients reflect active gRPC channels, for both ADS and EDS.
adsClients map[string]*Connection
...
}
回头看Server.go中initDiscoveryService()实际是启动了XDSServer
启动协程,处理发送事宜
循环中读取队列内容,推送到客户端client.pushChannel
func doSendPushes(stopCh <-chan struct{}, semaphore chan struct{}, queue *PushQueue) {
for {
select {
case <-stopCh:
return
default:
// We can send to it until it is full, then it will block until a pushes finishes and reads from it.
// This limits the number of pushes that can happen concurrently
semaphore <- struct{}{} // Get the next proxy to push. This will block if there are no updates required.
client, push, shuttingdown := queue.Dequeue()
if shuttingdown {
return
}
recordPushTriggers(push.Reason...)
// Signals that a push is done by reading from the semaphore, allowing another send on it.
doneFunc := func() {
queue.MarkDone(client)
<-semaphore
} proxiesQueueTime.Record(time.Since(push.Start).Seconds())
var closed <-chan struct{}
if client.stream != nil {
closed = client.stream.Context().Done()
} else {
closed = client.deltaStream.Context().Done()
}
go func() {
pushEv := &Event{
pushRequest: push,
done: doneFunc,
} select {
case client.pushChannel <- pushEv:
return
case <-closed: // grpc stream was closed
doneFunc()
log.Infof("Client closed connection %v", client.conID)
}
}()
}
}
}
client是ads.go,代表了xds真正的客户端,这里包含了xds grpc的客户端以及连接等信息,也包含了相关操作
这里另外条路是GRPC接收端,通过StreamAggregatedResource方法接收请求,
// StreamAggregatedResources implements the ADS interface.
func (s *DiscoveryServer) StreamAggregatedResources(stream DiscoveryStream) error {
return s.Stream(stream)
}
func (s *DiscoveryServer) Stream(stream DiscoveryStream) error {
......认证、连接
con := newConnection(peerAddr, stream) // Do not call: defer close(con.pushChannel). The push channel will be garbage collected
// when the connection is no longer used. Closing the channel can cause subtle race conditions
// with push. According to the spec: "It's only necessary to close a channel when it is important
// to tell the receiving goroutines that all data have been sent." // Block until either a request is received or a push is triggered.
// We need 2 go routines because 'read' blocks in Recv().
go s.receive(con, ids) // Wait for the proxy to be fully initialized before we start serving traffic. Because
// initialization doesn't have dependencies that will block, there is no need to add any timeout
// here. Prior to this explicit wait, we were implicitly waiting by receive() not sending to
// reqChannel and the connection not being enqueued for pushes to pushChannel until the
// initialization is complete.
<-con.initialized for {// If there wasn't already a request, poll for requests and pushes. Note: if we have a huge
// amount of incoming requests, we may still send some pushes, as we do not `continue` above;
// however, requests will be handled ~2x as much as pushes. This ensures a wave of requests
// cannot completely starve pushes. However, this scenario is unlikely.
select {
case req, ok := <-con.reqChan:
if ok {
if err := s.processRequest(req, con); err != nil { // envoy->istiod
return err
}
} else {
// Remote side closed connection or error processing the request.
return <-con.errorChan
}
case pushEv := <-con.pushChannel:
err := s.pushConnection(con, pushEv) // istiod -> envoy
pushEv.done()
if err != nil {
return err
}
case <-con.stop:
return nil
}
}
}
pilot-agent启动
main.go -> cmd.go -> agent.go
// Run is a non-blocking call which returns either an error or a function to await for completion.
func (a *Agent) Run(ctx context.Context) (func(), error) {
......
a.xdsProxy, err = initXdsProxy(a)
......if a.cfg.GRPCBootstrapPath != "" {
if err := a.generateGRPCBootstrap(); err != nil {
return nil, fmt.Errorf("failed generating gRPC XDS bootstrap: %v", err)
}
}if !a.EnvoyDisabled() {
err = a.initializeEnvoyAgent(ctx)
if err != nil {
return nil, fmt.Errorf("failed to initialize envoy agent: %v", err)
} a.wg.Add(1)
go func() {
defer a.wg.Done()
// This is a blocking call for graceful termination.
a.envoyAgent.Run(ctx)
}()
} else if a.WaitForSigterm() {
// wait for SIGTERM and perform graceful shutdown
a.wg.Add(1)
go func() {
defer a.wg.Done()
<-ctx.Done()
}()
}
return a.wg.Wait, nil
}
好文
https://cloud.tencent.com/developer/article/2177603
Istio思考往前一小步~系列一的更多相关文章
- 前端面霸系列(1):doctype 、Quirks Mode & Standards Mode 、document.compatMode
近几日,气压猛降,雾霾铺天盖地,眼看一场腥风血雨就要在前端江湖爆发,这场战争不仅是百度.腾讯.阿狸.搜狐网易新浪等江湖豪门抢夺人才的大战,也是诸位江湖人士重新洗牌的好时机.每年10月,江湖的波动胜过华 ...
- ABP开发框架前后端开发系列---(3)框架的分层和文件组织
在前面随笔<ABP开发框架前后端开发系列---(2)框架的初步介绍>中,我介绍了ABP应用框架的项目组织情况,以及项目中领域层各个类代码组织,以便基于数据库应用的简化处理.本篇随笔进一步对 ...
- ABP开发框架前后端开发系列---(2)框架的初步介绍
在前面随笔<ABP开发框架前后端开发系列---(1)框架的总体介绍>大概介绍了这个ABP框架的主要特点,以及介绍了我对这框架的Web API应用优先的一些看法,本篇继续探讨ABP框架的初步 ...
- ABP开发框架前后端开发系列---(10)Web API调用类的简化处理
在较早期的随笔<ABP开发框架前后端开发系列---(5)Web API调用类在Winform项目中的使用>已经介绍了Web API调用类的封装处理,虽然这些调用类我们可以使用代码生成工具快 ...
- ABP开发框架前后端开发系列---(5)Web API调用类在Winform项目中的使用
在前面几篇随笔介绍了我对ABP框架的改造,包括对ABP总体的介绍,以及对各个业务分层的简化,Web API 客户端封装层的设计,使得我们基于ABP框架的整体方案越来越清晰化, 也越来越接近实际的项目开 ...
- ABP开发框架前后端开发系列---(4)Web API调用类的封装和使用
在前面随笔介绍ABP应用框架的项目组织情况,以及项目中领域层各个类代码组织,以及简化了ABP框架的各个层的内容,使得我们项目结构更加清晰.上篇随笔已经介绍了字典模块中应用服务层接口的实现情况,并且通过 ...
- ABP开发框架前后端开发系列---(11)菜单的动态管理
在前面随笔<ABP开发框架前后端开发系列---(9)ABP框架的权限控制管理>中介绍了基于ABP框架服务构建的Winform客户端,客户端通过Web API调用的方式进行获取数据,从而实现 ...
- ABP开发框架前后端开发系列---(9)ABP框架的权限控制管理
在前面两篇随笔<ABP开发框架前后端开发系列---(7)系统审计日志和登录日志的管理>和<ABP开发框架前后端开发系列---(8)ABP框架之Winform界面的开发过程>开始 ...
- ABP开发框架前后端开发系列---(8)ABP框架之Winform界面的开发过程
在前面随笔介绍的<ABP开发框架前后端开发系列---(7)系统审计日志和登录日志的管理>里面,介绍了如何改进和完善审计日志和登录日志的应用服务端和Winform客户端,由于篇幅限制,没有进 ...
- ABP开发框架前后端开发系列---(12)配置模块的管理
一般来说,一个系统或多或少都会涉及到一些系统参数或者用户信息的配置,而ABP框架也提供了一套配置信息的管理模块,ABP框架的配置信息,必须提前定义好配置的各项内容,然后才能在系统中初始化或者通过接口查 ...
随机推荐
- Hide-and-Seek: Forcing a Network to be Meticulous for Weakly-Supervised Object and Action Localization概述
0.前言 相关资料: paper 网站 论文解读(知乎,CSDN) 论文基本信息: 领域:弱监督动作定位 发表时间:ICCV2017 1.针对的问题 大多数网络只识别图像最具有鉴别力的部分,不是所有相 ...
- 1996. 游戏中弱角色的数量 (Medium)
问题描述 1996. 游戏中弱角色的数量 (Medium) 你正在参加一个多角色游戏,每个角色都有两个主要属性: 攻击 和 防御 .给你一个二维整数数组 properties ,其中 properti ...
- 统计学习导论之R语言应用(三):线性回归R语言代码实战
统计学习导论(ISLR) 参考资料 The Elements of Statistical Learning An Introduction to Statistical Learning 统计学习导 ...
- iis express添加网站,并启动
1.查看网站列表 C:\Program Files (x86)\Microsoft Visual Studio 12.0>"C:\Program Files\IIS Express\a ...
- [node]启动一个简单的node接口
来自知乎. 需要先安装一个node,安装过程不表. 新建文件: nodeServer.js // 复制这个文件到目录,再输入一下命令: // npm i express // node nodeSer ...
- Chrome 中设置默认搜索引擎为 google.com
https://google.com/search?q=%s&ref=opensearch&safe=off 那么就可以使搜索结果默认在新标签页打开,同时关闭了搜索结果的安全过滤. ...
- docker容器监控系统
Cadvisor+InfluxDB+Grafana Cadvisor Cadvisor是检测单节点资源信息的工具,提供了一个http接口的查询界面,可以和其他工具整合使用,Cadvisor既可以采集宿 ...
- 20211306丁文博 python技能树、CSDN MarkDown编辑器
测评内容: python技能树.CSDN MarkDown编辑器 指导老师:王志强 班级:2113 学号:20211306 姓名:丁文博 https://blog.csdn.net/weixin_62 ...
- centos 挂载共享目录
共享目录://10.253.47.102/share/文件传递暂用 将共享路径挂载到centos /etc/fstab 添加一行配置,mount -a 生效
- poi解析Excel2007海量数据
处理excel,开源的javaApI提供了两种,一种是jxl,一种是poi.poi提供的功能较多,所以我用的是poi. poi有两种模式,一个是用户模式(HSSFworkbook:支持Excel200 ...