当前位置: 首页 > 编程日记 > 正文

从HelloWorld看Knative Serving代码实现

为什么80%的码农都做不了架构师?>>>   hot3.png

摘要: Knative Serving以Kubernetes和Istio为基础,支持无服务器应用程序和函数的部署并提供服务。我们从部署一个HelloWorld示例入手来分析Knative Serving的代码细节。

概念先知

官方给出的这几个资源的关系图还是比较清晰的:

1.Service: 自动管理工作负载整个生命周期。负责创建route,configuration以及每个service更新的revision。通过Service可以指定路由流量使用最新的revision,还是固定的revision。
2.Route:负责映射网络端点到一个或多个revision。可以通过多种方式管理流量。包括灰度流量和重命名路由。
3.Configuration:负责保持deployment的期望状态,提供了代码和配置之间清晰的分离,并遵循应用开发的12要素。修改一次Configuration产生一个revision。
4.Revision:Revision资源是对工作负载进行的每个修改的代码和配置的时间点快照。Revision是不可变对象,可以长期保留。

看一个简单的示例

我们开始运行官方hello-world示例,看看会发生什么事情:

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:name: helloworld-gonamespace: default
spec:runLatest: // RunLatest defines a simple Service. It will automatically configure a route that keeps the latest ready revision from the supplied configuration running.configuration:revisionTemplate:spec:container:image: registry.cn-shanghai.aliyuncs.com/larus/helloworld-goenv:- name: TARGETvalue: "Go Sample v1"

查看 knative-ingressgateway:

kubectl get svc knative-ingressgateway -n istio-system

查看服务访问:DOMAIN

kubectl get ksvc helloworld-go  --output=custom-columns=NAME:.metadata.name,DOMAIN:.status.domain


这里直接使用cluster ip即可访问

curl -H "Host: helloworld-go.default.example.com" http://10.96.199.35

目前看一下服务是部署ok的。那我们看一下k8s里面创建了哪些资源:

我们可以发现通过Serving,在k8s中创建了2个service和1个deployment:

那么究竟Serving中做了哪些处理,接下来我们分析一下Serving源代码

源代码分析

Main

先看一下各个组件的控制器启动代码,这个比较好找,在/cmd/controller/main.go中。
依次启动configuration、revision、route、labeler、service和clusteringress控制器。

...
controllers := []*controller.Impl{configuration.NewController(opt,configurationInformer,revisionInformer,),revision.NewController(opt,revisionInformer,kpaInformer,imageInformer,deploymentInformer,coreServiceInformer,endpointsInformer,configMapInformer,buildInformerFactory,),route.NewController(opt,routeInformer,configurationInformer,revisionInformer,coreServiceInformer,clusterIngressInformer,),labeler.NewRouteToConfigurationController(opt,routeInformer,configurationInformer,revisionInformer,),service.NewController(opt,serviceInformer,configurationInformer,routeInformer,),clusteringress.NewController(opt,clusterIngressInformer,virtualServiceInformer,),}
...

Service

首先我们要从Service来看,因为我们一开始的输入就是Service资源。在/pkg/reconciler/v1alpha1/service/service.go。
比较简单,就是根据Service创建Configuration和Route资源

func (c *Reconciler) reconcile(ctx context.Context, service *v1alpha1.Service) error {...configName := resourcenames.Configuration(service)config, err := c.configurationLister.Configurations(service.Namespace).Get(configName)if errors.IsNotFound(err) {config, err = c.createConfiguration(service)...routeName := resourcenames.Route(service)route, err := c.routeLister.Routes(service.Namespace).Get(routeName)if errors.IsNotFound(err) {route, err = c.createRoute(service)...
}

Route

/pkg/reconciler/v1alpha1/route/route.go
看一下Route中reconcile做了哪些处理:
1.判断是否有Ready的Revision可进行traffic
2.设置目标流量的Revision(runLatest:使用最新的版本;pinned:固定版本,不过已弃用;release:通过允许在两个修订版之间拆分流量,逐步扩大到新修订版,用于替换pinned。manual:手动模式,目前来看并未实现)
3.创建ClusterIngress:Route不直接依赖于VirtualService[https://istio.io/docs/reference/config/istio.networking.v1alpha3/#VirtualService] ,而是依赖一个中间资源ClusterIngress,它可以针对不同的网络平台进行不同的协调。目前实现是基于istio网络平台。
4.创建k8s service:这个Service主要为Istio路由提供域名访问。

func (c *Reconciler) reconcile(ctx context.Context, r *v1alpha1.Route) error {....// 基于是否有Ready的Revisiontraffic, err := c.configureTraffic(ctx, r)if traffic == nil || err != nil {// Traffic targets aren't ready, no need to configure child resources.return err}logger.Info("Updating targeted revisions.")// In all cases we will add annotations to the referred targets.  This is so that when they become// routable we can know (through a listener) and attempt traffic configuration again.if err := c.reconcileTargetRevisions(ctx, traffic, r); err != nil {return err}// Update the information that makes us Addressable.r.Status.Domain = routeDomain(ctx, r)r.Status.DeprecatedDomainInternal = resourcenames.K8sServiceFullname(r)r.Status.Address = &duckv1alpha1.Addressable{Hostname: resourcenames.K8sServiceFullname(r),}// Add the finalizer before creating the ClusterIngress so that we can be sure it gets cleaned up.if err := c.ensureFinalizer(r); err != nil {return err}logger.Info("Creating ClusterIngress.")desired := resources.MakeClusterIngress(r, traffic, ingressClassForRoute(ctx, r))clusterIngress, err := c.reconcileClusterIngress(ctx, r, desired)if err != nil {return err}r.Status.PropagateClusterIngressStatus(clusterIngress.Status)logger.Info("Creating/Updating placeholder k8s services")if err := c.reconcilePlaceholderService(ctx, r, clusterIngress); err != nil {return err}r.Status.ObservedGeneration = r.Generationlogger.Info("Route successfully synced")return nil
}

看一下helloworld-go生成的Route资源文件:

apiVersion: serving.knative.dev/v1alpha1
kind: Route
metadata:name: helloworld-gonamespace: default
...
spec:generation: 1traffic:- configurationName: helloworld-go percent: 100
status:
...domain: helloworld-go.default.example.comdomainInternal: helloworld-go.default.svc.cluster.localtraffic:- percent: 100 # 所有的流量通过这个revisionrevisionName: helloworld-go-00001 # 使用helloworld-go-00001 revision

这里可以看到通过helloworld-go配置, 找到了已经ready的helloworld-go-00001(Revision)。

Configuration

/pkg/reconciler/v1alpha1/configuration/configuration.go
1.获取当前Configuration对应的Revision, 若不存在则创建。
2.为Configuration设置最新的Revision
3.根据Revision是否readiness,设置Configuration的状态LatestReadyRevisionName

func (c *Reconciler) reconcile(ctx context.Context, config *v1alpha1.Configuration) error {...// First, fetch the revision that should exist for the current generation.lcr, err := c.latestCreatedRevision(config)if errors.IsNotFound(err) {lcr, err = c.createRevision(ctx, config)...    revName := lcr.Name// Second, set this to be the latest revision that we have created.config.Status.SetLatestCreatedRevisionName(revName)config.Status.ObservedGeneration = config.Generation// Last, determine whether we should set LatestReadyRevisionName to our// LatestCreatedRevision based on its readiness.rc := lcr.Status.GetCondition(v1alpha1.RevisionConditionReady)switch {case rc == nil || rc.Status == corev1.ConditionUnknown:logger.Infof("Revision %q of configuration %q is not ready", revName, config.Name)case rc.Status == corev1.ConditionTrue:logger.Infof("Revision %q of configuration %q is ready", revName, config.Name)created, ready := config.Status.LatestCreatedRevisionName, config.Status.LatestReadyRevisionNameif ready == "" {// Surface an event for the first revision becoming ready.c.Recorder.Event(config, corev1.EventTypeNormal, "ConfigurationReady","Configuration becomes ready")}// Update the LatestReadyRevisionName and surface an event for the transition.config.Status.SetLatestReadyRevisionName(lcr.Name)if created != ready {c.Recorder.Eventf(config, corev1.EventTypeNormal, "LatestReadyUpdate","LatestReadyRevisionName updated to %q", lcr.Name)}
...
}

看一下helloworld-go生成的Configuration资源文件:

apiVersion: serving.knative.dev/v1alpha1
kind: Configuration
metadata:name: helloworld-gonamespace: default...
spec:generation: 1revisionTemplate:metadata:creationTimestamp: nullspec:container:env:- name: TARGETvalue: Go Sample v1image: registry.cn-shanghai.aliyuncs.com/larus/helloworld-goname: ""resources: {}timeoutSeconds: 300
status:...latestCreatedRevisionName: helloworld-go-00001latestReadyRevisionName: helloworld-go-00001observedGeneration: 1

我们可以发现LatestReadyRevisionName设置了helloworld-go-00001(Revision)。

Revision

/pkg/reconciler/v1alpha1/revision/revision.go
1.获取build进度
2.设置镜像摘要
3.创建deployment
4.创建k8s service:根据Revision构建服务访问Service
5.创建fluentd configmap
6.创建KPA
感觉这段代码写的很优雅,函数执行过程写的很清晰,值得借鉴。另外我们也可以发现,目前knative只支持deployment的工作负载

func (c *Reconciler) reconcile(ctx context.Context, rev *v1alpha1.Revision) error {...if err := c.reconcileBuild(ctx, rev); err != nil {return err}bc := rev.Status.GetCondition(v1alpha1.RevisionConditionBuildSucceeded)if bc == nil || bc.Status == corev1.ConditionTrue {// There is no build, or the build completed successfully.phases := []struct {name stringf    func(context.Context, *v1alpha1.Revision) error}{{name: "image digest",f:    c.reconcileDigest,}, {name: "user deployment",f:    c.reconcileDeployment,}, {name: "user k8s service",f:    c.reconcileService,}, {// Ensures our namespace has the configuration for the fluentd sidecar.name: "fluentd configmap",f:    c.reconcileFluentdConfigMap,}, {name: "KPA",f:    c.reconcileKPA,}}for _, phase := range phases {if err := phase.f(ctx, rev); err != nil {logger.Errorf("Failed to reconcile %s: %v", phase.name, zap.Error(err))return err}}}
...
}

最后我们看一下生成的Revision资源:

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:name: helloworld-gonamespace: default...
spec:generation: 1runLatest:configuration:revisionTemplate:spec:container:env:- name: TARGETvalue: Go Sample v1image: registry.cn-shanghai.aliyuncs.com/larus/helloworld-gotimeoutSeconds: 300
status:address:hostname: helloworld-go.default.svc.cluster.local...domain: helloworld-go.default.example.comdomainInternal: helloworld-go.default.svc.cluster.locallatestCreatedRevisionName: helloworld-go-00001latestReadyRevisionName: helloworld-go-00001observedGeneration: 1traffic:- percent: 100revisionName: helloworld-go-00001

这里我们可以看到访问域名helloworld-go.default.svc.cluster.local,以及当前revision的流量配比(100%)
这样我们分析完之后,现在打开Serving这个黑盒

最后

这里只是基于简单的例子,分析了主要的业务流程处理代码。对于activator(如何唤醒业务容器),autoscaler(Pod如何自动缩为0)等代码实现有兴趣的同学可以一起交流。

参考

https://github.com/knative/docs/tree/master/docs/serving

作者:元毅

原文链接​

本文为云栖社区原创内容,未经允许不得转载。

转载于:https://my.oschina.net/yunqi/blog/3052562

相关文章:

svo_note

SVO论文笔记1.frame overviews2. Motion Estimate Thread2.1 Sparse Model-based Image Alignment 基于稀疏点亮度的位姿预估2.2 Relaxation Through Feature Alignment 基于图块的特征点匹配2.3 Pose and Structure Refinement3 Mapping Thread3.1 depth-filter3.2 初始化参考…

Druid 配置 wallfilter

这个文档提供基于Spring的各种配置方式 使用缺省配置的WallFilter <bean id"dataSource" class"com.alibaba.druid.pool.DruidDataSource" init-method"init" destroy-method"close">...<property name"filters" v…

vue下的bootstrap table + jquery treegrid, treegrid无法渲染的问题

在mian.js导入的包如下&#xff1a;该bootstrap-table-treegrid.js需要去下载&#xff0c;在复制到jquery-treegrid/js/ 1 import $ from jquery 2 import bootstrap/dist/css/bootstrap.min.css 3 import bootstrap/dist/js/bootstrap.min 4 import bootstrap-table/dist/boot…

内存和缓存的区别

今天看书的时候又看到了内存和缓存&#xff0c;之所以说又&#xff0c;是因为之前遇到过查过资料&#xff0c;但是现在又忘了(图侵删)。 所以又复习一遍&#xff0c;记录一下&#xff0c;有所纰漏的地方&#xff0c;欢迎指正。 同志们&#xff0c;上图并不是内存和缓存中的任何…

【Boost】noncopyable:不可拷贝

【CSDN】&#xff1a;boost::noncopyable解析 【Effective C】&#xff1a;条款06_若不想使用编译器自动生成地函数&#xff0c;就该明确拒绝 1.example boost::noncopyable 为什么要boost::noncopyable 在c中定义一个类的时候&#xff0c;如果不明确定义拷贝构造函数和拷贝赋…

BigData NoSQL —— ApsaraDB HBase数据存储与分析平台概览

一、引言时间到了2019年&#xff0c;数据库也发展到了一个新的拐点&#xff0c;有三个明显的趋势&#xff1a; 越来越多的数据库会做云原生(CloudNative)&#xff0c;会不断利用新的硬件及云本身的优势打造CloudNative数据库&#xff0c;国内以阿里云的Cloud HBase、POLARDB为代…

ubuntu clion 创建桌面快捷方式

ubuntu clion 创建桌面快捷方式 首先在终端下输入 cd /usr/share/applications/进入applications目录下&#xff0c;建立一个clion.desktop文件 sudo touch clion.desktop然后在vim命令下编辑该文件 sudo vim clion.desktop进入vim后&#xff0c;按i插入开始编辑该文件&…

Flex 布局:语法篇

2019独角兽企业重金招聘Python工程师标准>>> 布局的传统解决方案&#xff0c;基于盒状模型&#xff0c;依赖 display 属性 position 属性 float 属性。它对于那些特殊布局非常不方便&#xff0c;比如&#xff0c;垂直居中就不容易实现。 2009年&#xff0c;W3C 提…

特征运动点估计

cv::Mat getRansacMat(const std::vector<cv::DMatch>& matches, const std::vector<cv::KeyPoint>& keypoints1, const std::vector<cv::KeyPoint>& keypoints2, std::vector<cv::DMatch>& outMatches) {// 转换特征点格式std::vecto…

Vue+Element-ui+二级联动封装组件

通过父子组件传值 父组件&#xff1a; 1 <template>2 <linkage :citysList"citysList" :holder"holder" saveId"saveId"></linkage>3 </template>4 <script>5 import linkage from ./common/linkage6 export de…

MOG2 成员函数参数设定

pMOG2->setDetectShadows(true); // 背景模型影响帧数 默认为500 pMOG2->setHistory(1000); // 模型匹配阈值 pMOG2->setVarThreshold(50); // 阴影阈值 pMOG2->setShadowThreshold(0.7);前景中模型参数&#xff0c;设置为0表示背景&#xff0c;255为前景&#xff…

webpack 大法好 ---- 基础概念与配置(1)

再一次见面&#xff01; Light 还是太太太懒了&#xff0c;距离上一篇没啥营养的文章已经过去好多天。今天为大家介绍介绍 webpack 最基本的概念&#xff0c;以及简单的配置&#xff0c;让你能快速得搭建一个可用的 webpack 开发环境。 webpack的安装 webpack 运行于 node 环境…

Zookeeper迁移(扩容/缩容)

zookeeper选举原理在迁移前有必要了解zookeeper的选举原理&#xff0c;以便更科学的迁移。快速选举FastLeaderElectionzookeeper默认使用快速选举&#xff0c;在此重点了解快速选举&#xff1a;向集群中的其他zookeeper建立连接&#xff0c;并且只有myid比对方大的连接才会被接…

SVO Without ROS环境搭建

Installation: Plain CMake (No ROS) 首先&#xff0c;建立工作目录&#xff1a;workspace&#xff0c;然后把下面的需要的都在该目录下进行. mkdir workspace cd workspace Boost - c Librairies (thread and system are needed) sudo apt-get install libboost-all-dev Eige…

BackgroundSubtractorGMG 背景建模

#include <opencv2/bgsegm.hpp> #include <opencv2/video.hpp> #include <opencv2/opencv.hpp> #include <iostream> #include <sstream> using namespace cv; using namespace std; using namespace bgsegm; // GMG目标建模检测 void detectBac…

启动webpack-dev-server只能本机访问的解决办法

修改package.json的dev启动设置&#xff0c;增加--host 0.0.0.0启动后localhost更换为本机IP即可访问

TCP/IP:IP选项处理

引言 IP输入函数要对IP 进行选项处理&#xff0c;。RFC791和1122规定了IP选项和处理规则。一个IP首部可以跟40个字节的选项。 选项格式 选项的格式&#xff0c;分为两种类型&#xff0c;单字节和多字节。 ip_dooptions函数 这个函数用于判断分组转发。用常量位移访问IP选项字段…

【SVO2.0 安装编译】Ubuntu 20.04 + Noetic

ways one 链接: https://pan.baidu.com/s/1ZAkeD64wjFsDHfpCm1CB1w 提取码: kxx2 (downloads and use idirectly) ways two: [SVO2-OPEN: https://github.com/uzh-rpg/rpg_svo_pro_open](https://github.com/DEARsunshine/rpg_svo_pro_open)git挂梯子 如果各位终端无法挂梯…

人眼目标检测初始化

// 初始化摄像头读取视频流cv::VideoCapture cap(0);// 宽高设置为320*256cap.set(CV_CAP_PROP_FRAME_WIDTH, 320);cap.set(CV_CAP_PROP_FRAME_HEIGHT, 256);// 读取级联分类器// 文件存放在opencv\sources\data\haarcascades bool flagGlasses false;if(flagGlasses){face_ca…

Qt之界面换肤

简述 常用的软件基本都有换肤功能&#xff0c;例如&#xff1a;QQ、360、迅雷等。换肤其实很简单&#xff0c;并没有想象中那么难&#xff0c;利用前面分享过的QSS系列文章&#xff0c;沃我们完全可以实现各种样式的定制&#xff01; 简述实现原理效果新建QSS文件编写QSS代码加…

mongDB的常用操作总结

目录 常用查询:查询一条数据查询子元素集合:image.idgte: 大于等于,lte小于等于...查询字段不存在的数据not查询数量:常用更新更新第一条数据的一个字段:更新一条数据的多个字段:常用删除删除:常用查询: 查询一条数据 精确匹配is Query(Criteria.where("id").is(id))…

【GTSAM】GTSAM学习

1 what GTSAM ? GTSAM 是一个在机器人领域和计算机视觉领域用于平滑&#xff08;smoothing&#xff09;和建图&#xff08;mapping&#xff09;的C库。它与g2o不同的是&#xff0c;g2o采用稀疏矩阵的方式求解一个非线性优化问题&#xff0c;而GTSAM是采用因子图&#xff08;f…

人脸、人眼检测与跟踪

#include <opencv2/opencv.hpp> #include <iostream> #include <vector> using namespace cv;CascadeClassifier face_cascade; CascadeClassifier eye_cascade;// 人眼检测 int detectEye(cv::Mat& im, cv::Mat& tpl, cv::Rect& rect) {std::v…

linux下jdk简单配置记录

记录哈&#xff0c;搭建环境的时候&#xff0c;copy使用方便。 vim /etc/profile export JAVA_HOME/usr/java/jdk1.7.0_79export PATH$JAVA_HOME/bin:$PATHexport CLASSPATH.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jarexport JRE_HOME$JAVA_HOME/jreexport LANGzh_CN.UT…

Ubuntu中Could not get lock /var/lib/dpkg/lock解决方案

关于Ubuntu中Could not get lock /var/lib/dpkg/lock解决方案 转载于:https://www.cnblogs.com/daemonFlY/p/10916812.html

so库方法原理

动态库 So库&#xff0c;又动态名库&#xff0c;是Linux下最常见的文件之一&#xff0c;是一种ELF文件。这种so库是程序运行时&#xff0c;才会将这些需要的代码拷贝到对应的内存中。但程序运行时&#xff0c;这些地址早已经确定&#xff0c;那程序引用so库中的这些代码地址如…

上传图片,多图上传,预览功能,js原生无依赖

最近很好奇前端的文件上传功能&#xff0c;因为公司要求做一个支持图片预览的图片上传插件&#xff0c;所以自己搜了很多相关的插件&#xff0c;虽然功能很多&#xff0c;但有些地方不能根据公司的想法去修改&#xff0c;而且需要依赖jQuery或Bootstrap库&#xff0c;所以我就想…

springboot 简单自定义starter - beetl

使用idea新建springboot项目beetl-spring-boot-starter 首先添加pom依赖 packaging要设置为jar不能设置为pom<packaging>jar</packaging> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web&…

cmake生成so包并调用(C++project,build,cmake)

1. 目录结构 2 . downloads 2.1 build module process CMakeLists.txt > cmake_minimum_required(VERSION 3.5)if(CMAKE_COMPILER_IS_GNUCC)message("COMPILER IS GNUCC")ADD_DEFINITIONS ( -stdc11 ) endif(CMAKE_COMPILER_IS_GNUCC)SET(CMAKE_CXX_FLAGS_DEBU…

人眼模板匹配自动跟踪

void trackEye(cv::Mat& im, cv::Mat& tpl, cv::Rect& rect) {// 人眼位置cv::Size pSize(rect.width * 2, rect.height * 2);// 矩形区域cv::Rect tRect(rect pSize - cv::Point(pSize.width/2, pSize.height/2));tRect & cv::Rect(0, 0, im.cols, im.rows);…