保健励志美文体育育儿作文
投稿投诉
作文动态
热点娱乐
育儿情感
教程科技
体育养生
教案探索
美文旅游
财经日志
励志范文
论文时尚
保健游戏
护肤业界

整合k8s系列02服务器端应用

  适用版本:Kubernetesv1。22〔stable〕简介
  服务器端应用协助用户、控制器通过声明式配置的方式管理他们的资源。客户端可以发送完整描述的目标(Afullyspecifiedintent),声明式地创建和或修改对象。
  一个完整描述的目标并不是一个完整的对象,仅包括能体现用户意图的字段和值。该目标(intent)可以用来创建一个新对象,也可以通过服务器来实现与现有对象的合并。
  系统支持多个应用者(appliers)在同一个对象上开展协作。
  字段管理(fieldmanagement)机制追踪对象字段的变化。当一个字段值改变时,其所有权从当前管理器(manager)转移到施加变更的管理器。当尝试将新配置应用到一个对象时,如果字段有不同的值,且由其他管理器管理,将会引发冲突。冲突引发警告信号:此操作可能抹掉其他协作者的修改。冲突可以被刻意忽略,这种情况下,值将会被改写,所有权也会发生转移。
  当你从配置文件中删除一个字段,然后应用这个配置文件,这将触发服务端应用检查此字段是否还被其他字段管理器拥有。如果没有,那就从活动对象中删除该字段;如果有,那就重置为默认值。该规则同样适用于list或map项目。
  服务器端应用既是原有kubectlapply的替代品,也是控制器发布自身变化的一个简化机制。
  如果你启用了服务器端应用,控制平面就会跟踪被所有新创建对象管理的字段。字段管理
  相对于通过kubectl管理的注解lastapplied,服务器端应用使用了一种更具声明式特点的方法:它持续的跟踪用户的字段管理,而不仅仅是最后一次的执行状态。这就意味着,作为服务器端应用的一个副作用,关于用哪一个字段管理器负责管理对象中的哪个字段的这类信息,都要对外界开放了。
  用户管理字段这件事,在服务器端应用的场景中,意味着用户依赖并期望字段的值不要改变。最后一次对字段值做出断言的用户将被记录到当前字段管理器。这可以通过发送POST、PUT、或非应用(nonapply)方式的PATCH等命令来修改字段值的方式实现,或通过把字段放在配置文件中,然后发送到服务器端应用的服务端点的方式实现。当使用服务器端应用,尝试着去改变一个被其他人管理的字段,会导致请求被拒绝(在没有设置强制执行时,参见冲突)。
  如果两个或以上的应用者均把同一个字段设置为相同值,他们将共享此字段的所有权。后续任何改变共享字段值的尝试,不管由那个应用者发起,都会导致冲突。共享字段的所有者可以放弃字段的所有权,这只需从配置文件中删除该字段即可。
  字段管理的信息存储在managedFields字段中,该字段是对象的metadata中的一部分。
  服务器端应用创建对象的简单示例如下:apiVersion:v1kind:ConfigMapmetadata:name:testcmnamespace:defaultlabels:testlabel:testmanagedFields:manager:kubectloperation:ApplyapiVersion:v1time:20101010T0:00:00ZfieldsType:FieldsV1fieldsV1:f:metadata:f:labels:f:testlabel:{}f:data:f:key:{}data:key:somevalue
  上述对象在metadata。managedFields中包含了唯一的管理器。管理器由管理实体自身的基本信息组成,比如操作类型、API版本、以及它管理的字段。
  Note:该字段由API服务器管理,用户不应该改动它。
  不过,执行Update操作修改metadata。managedFields也是可实现的。强烈不鼓励这么做,但当发生如下情况时,比如managedFields进入不一致的状态(显然不应该发生这种情况),这么做也是一个合理的尝试。
  managedFields的格式在API文档中描述。冲突
  冲突是一种特定的错误状态,发生在执行Apply改变一个字段,而恰巧该字段被其他用户声明过主权时。这可以防止一个应用者不小心覆盖掉其他用户设置的值。冲突发生时,应用者有三种办法来解决它:覆盖前值,成为唯一的管理器:如果打算覆盖该值(或应用者是一个自动化部件,比如控制器),应用者应该设置查询参数force为true,然后再发送一次请求。这将强制操作成功,改变字段的值,从所有其他管理器的managedFields条目中删除指定字段。不覆盖前值,放弃管理权:如果应用者不再关注该字段的值,可以从配置文件中删掉它,再重新发送请求。这就保持了原值不变,并从managedFields的应用者条目中删除该字段。不覆盖前值,成为共享的管理器:如果应用者仍然关注字段值,并不想覆盖它,他们可以在配置文件中把字段的值改为和服务器对象一样,再重新发送请求。这样在不改变字段值的前提下,就实现了字段管理被应用者和所有声明了管理权的其他的字段管理器共享。管理器
  管理器识别出正在修改对象的工作流程(在冲突时尤其有用),管理器可以通过修改请求的参数fieldManager指定。虽然kubectl默认发往kubectl服务端点,但它则请求到应用的服务端点(applyendpoint)。对于其他的更新,它默认的是从用户代理计算得来。应用和更新
  此特性涉及两类操作,分别是Apply(内容类型为applicationapplypatchyaml的PATCH请求)和Update(所有修改对象的其他操作)。这两类操作都会更新字段managedFields,但行为表现有一点不同。
  Note:
  不管你提交的是JSON数据还是YAML数据,都要使用applicationapplypatchyaml作为ContentType的值。
  所有的JSON文档都是合法的YAML。
  例如,在冲突发生的时候,只有apply操作失败,而update则不会。此外,apply操作必须通过提供一个fieldManager查询参数来标识自身,而此查询参数对于update操作则是可选的。最后,当使用apply命令时,你不能在应用中的对象中持有managedFields。
  一个包含多个管理器的对象,示例如下:apiVersion:v1kind:ConfigMapmetadata:name:testcmnamespace:defaultlabels:testlabel:testmanagedFields:manager:kubectloperation:ApplyapiVersion:v1fields:f:metadata:f:labels:f:testlabel:{}manager:kubecontrollermanageroperation:UpdateapiVersion:v1time:20190330T16:00:00。000Zfields:f:data:f:key:{}data:key:newvalue
  在这个例子中,第二个操作被管理器kubecontrollermanager以Update的方式运行。此update更改data字段的值,并使得字段管理器被改为kubecontrollermanager。
  如果把update操作改为Apply,那就会因为所有权冲突的原因,导致操作失败。合并策略
  由服务器端应用实现的合并策略,提供了一个总体更稳定的对象生命周期。服务器端应用试图依据负责管理它们的主体来合并字段,而不是根据值来否决。这么做是为了多个主体可以更新同一个对象,且不会引起意外的相互干扰。
  当用户发送一个完整描述的目标对象到服务器端应用的服务端点,服务器会将它和活动对象做一次合并,如果两者中有重复定义的值,那就以配置文件的为准。如果配置文件中的项目集合不是此用户上一次操作项目的超集,所有缺少的、没有其他应用者管理的项目会被删除。关于合并时用来做决策的对象规格的更多信息,参见sigs。k8s。iostructuredmergediff。
  Kubernetes1。16和1。17中添加了一些标记,允许API开发人员描述由list、map、和structs支持的合并策略。这些标记可应用到相应类型的对象,在Go文件或在CRD的OpenAPI的模式中定义:
  Golang标记
  OpenAPIextension
  可接受的值
  描述
  引入版本
  listType
  xkuberneteslisttype
  atomicsetmap
  适用于list。set适用于仅包含标量元素的列表。这些元素必须是不重复的。map仅适用于包含嵌套类型的列表。列表中的键(参见listMapKey)不可以重复。atomic适用于任何类型的列表。如果配置为atomic,则合并时整个列表会被替换掉。任何时候,只有一个管理器负责管理指定列表。如果配置为set或map,不同的管理器也可以分开管理条目。
  1。16
  listMapKey
  xkuberneteslistmapkeys
  字段名称的列表,例如,〔port,protocol〕
  仅当listTypemap时适用。取值为字段名称的列表,这些字段值的组合能够唯一标识列表中的条目。尽管可以存在多个键,listMapKey是单数的,这是因为键名需要在Go类型中各自独立指定。键字段必须是标量。
  1。16
  mapType
  xkubernetesmaptype
  atomicgranular
  适用于map。atomic指map只能被单个的管理器整个的替换。granular指map支持多个管理器各自更新自己的字段。
  1。17
  structType
  xkubernetesmaptype
  atomicgranular
  适用于structs;否则就像mapType有相同的用法和openapi注释。
  1。17
  若未指定listType,API服务器将patchMergeStrategymerge标记解释为listTypemap并且视对应的patchMergeKey标记为listMapKey取值。
  atomic列表类型是递归的。
  这些标记都是用源代码注释的方式给出的,不必作为字段标签(tag)再重复。拓扑变化时的兼容性
  在极少的情况下,CRD或者内置类型的作者可能希望更改其资源中的某个字段的拓扑配置,同时又不提升版本号。通过升级集群或者更新CRD来更改类型的拓扑信息与更新现有对象的结果不同。变更的类型有两种:一种是将字段从mapsetgranular更改为atomic,另一种是做逆向改变。
  当listType、mapType或structType从mapsetgranular改为atomic时,现有对象的整个列表、映射或结构的属主都会变为这些类型的元素之一的属主。这意味着,对这些对象的进一步变更会引发冲突。
  当一个列表、映射或结构从atomic改为mapsetgranular之一时,API服务器无法推导这些字段的新的属主。因此,当对象的这些字段再次被更新时不会引发冲突。出于这一原因,不建议将某类型从atomic改为mapsetgranular。
  以下面的自定义资源为例:apiVersion:example。comv1kind:Foometadata:name:foosamplemanagedFields:manager:manageroneoperation:ApplyapiVersion:example。comv1fields:f:spec:f:data:{}spec:data:key1:val1key2:val2
  在spec。data从atomic改为granular之前,managerone是spec。data字段及其所包含字段(key1和key2)的属主。当对应的CRD被更改,使得spec。data变为granular拓扑时,managerone继续拥有顶层字段spec。data(这意味着其他管理者想删除名为data的映射而不引起冲突是不可能的),但不再拥有key1和key2。因此,其他管理者可以在不引起冲突的情况下更改或删除这些字段。自定义资源
  默认情况下,服务器端应用把自定义资源看做非结构化数据。所有的键值(keys)就像struct的字段一样被处理,所有的list被认为是原子性的。
  如果自定义资源定义(CustomResourceDefinition,CRD)定义了一个模式,它包含类似以前合并策略章节中定义过的注解,这些注解将在合并此类型的对象时使用。在控制器中使用服务器端应用
  控制器的开发人员可以把服务器端应用作为简化控制器的更新逻辑的方式。读改写和或patch的主要区别如下所示:应用的对象必须包含控制器关注的所有字段。对于在控制器没有执行过应用操作之前就已经存在的字段,不能删除。(控制器在这种用例环境下,依然可以发送一个PATCHUPDATE)对象不必事先读取,resourceVersion不必指定。
  强烈推荐:设置控制器在冲突时强制执行,这是因为冲突发生时,它们没有其他解决方案或措施。转移所有权
  除了通过冲突解决方案提供的并发控制,服务器端应用提供了一些协作方式来将字段所有权从用户转移到控制器。
  最好通过例子来说明这一点。让我们来看看,在使用HorizontalPodAutoscaler资源和与之配套的控制器,且开启了Deployment的自动水平扩展功能之后,怎么安全的将replicas字段的所有权从用户转移到控制器。
  假设用户定义了Deployment,且replicas字段已经设置为期望的值:
  applicationssanginxdeployment。yamlapiVersion:appsv1kind:Deploymentmetadata:name:nginxdeploymentlabels:app:nginxspec:replicas:3selector:matchLabels:app:nginxtemplate:metadata:labels:app:nginxspec:containers:name:nginximage:nginx:1。14。2
  并且,用户使用服务器端应用,像这样创建Deployment:kubectlapplyfhttps:k8s。ioexamplesapplicationssanginxdeployment。yamlserverside
  然后,为Deployment启用HPA,例如:kubectlautoscaledeploymentnginxdeploymentcpupercent50min1max10
  现在,用户希望从他们的配置中删除replicas,所以他们总是和HPA控制器冲突。然而,这里存在一个竟态:在HPA需要调整replicas之前会有一个时间窗口,如果在HPA写入字段成为所有者之前,用户删除了replicas,那API服务器就会把replicas的值设为1,也就是默认值。这不是用户希望发生的事情,即使是暂时的。
  这里有两个解决方案:(基本操作)把replicas留在配置文件中;当HPA最终写入那个字段,系统基于此事件告诉用户:冲突发生了。在这个时间点,可以安全的删除配置文件。(高级操作)然而,如果用户不想等待,比如他们想为合作伙伴保持集群清晰,那他们就可以执行以下步骤,安全的从配置文件中删除replicas。
  首先,用户新定义一个只包含replicas字段的配置文件:
  applicationssanginxdeploymentreplicasonly。yamlapiVersion:appsv1kind:Deploymentmetadata:name:nginxdeploymentspec:replicas:3
  用户使用名为handovertohpa的字段管理器,应用此配置文件。kubectlapplyfhttps:k8s。ioexamplesapplicationssanginxdeploymentreplicasonly。yamlserversidefieldmanagerhandovertohpavalidatefalse
  如果应用操作和HPA控制器产生冲突,那什么都不做。冲突表明控制器在更早的流程中已经对字段声明过所有权。
  在此时间点,用户可以从配置文件中删除replicas。
  applicationssanginxdeploymentnoreplicas。yamlapiVersion:appsv1kind:Deploymentmetadata:name:nginxdeploymentlabels:app:nginxspec:selector:matchLabels:app:nginxtemplate:metadata:labels:app:nginxspec:containers:name:nginximage:nginx:1。14。2
  注意,只要HPA控制器为replicas设置了一个新值,该临时字段管理器将不再拥有任何字段,会被自动删除。这里不需要执行清理工作。在用户之间转移所有权
  通过在配置文件中把一个字段设置为相同的值,用户可以在他们之间转移字段的所有权,从而共享了字段的所有权。当用户共享了字段的所有权,任何一个用户可以从他的配置文件中删除该字段,并应用该变更,从而放弃所有权,并实现了所有权向其他用户的转移。与客户端应用的对比
  由服务器端应用实现的冲突检测和解决方案的一个结果就是,应用者总是可以在本地状态中得到最新的字段值。如果得不到最新值,下次执行应用操作时就会发生冲突。解决冲突三个选项的任意一个都会保证:此应用过的配置文件是服务器上对象字段的最新子集。
  这和客户端应用(ClientSideApply)不同,如果有其他用户覆盖了此值,过期的值被留在了应用者本地的配置文件中。除非用户更新了特定字段,此字段才会准确,应用者没有途径去了解下一次应用操作是否会覆盖其他用户的修改。
  另一个区别是使用客户端应用的应用者不能改变他们正在使用的API版本,但服务器端应用支持这个场景。从客户端应用升级到服务器端应用
  客户端应用方式时,用户使用kubectlapply管理资源,可以通过使用下面标记切换为使用服务器端应用。kubectlapplyserverside〔dryrunserver〕
  默认情况下,对象的字段管理从客户端应用方式迁移到kubectl触发的服务器端应用时,不会发生冲突。
  Caution:
  保持注解lastappliedconfiguration是最新的。从注解能推断出字段是由客户端应用管理的。任何没有被客户端应用管理的字段将引发冲突。
  举例说明,比如你在客户端应用之后,使用kubectlscale去更新replicas字段,可是该字段并没有被客户端应用所拥有,在执行kubectlapplyserverside时就会产生冲突。
  此操作以kubectl作为字段管理器来应用到服务器端应用。作为例外,可以指定一个不同的、非默认字段管理器停止的这种行为,如下面的例子所示。对于kubectl触发的服务器端应用,默认的字段管理器是kubectl。kubectlapplyserversidefieldmanagermymanager〔dryrunserver〕从服务器端应用降级到客户端应用
  如果你用kubectlapplyserverside管理一个资源,可以直接用kubectlapply命令将其降级为客户端应用。
  降级之所以可行,这是因为kubectlserversideapply会保存最新的lastappliedconfiguration注解。
  此操作以kubectl作为字段管理器应用到服务器端应用。作为例外,可以指定一个不同的、非默认字段管理器停止这种行为,如下面的例子所示。对于kubectl触发的服务器端应用,默认的字段管理器是kubectl。kubectlapplyserversidefieldmanagermymanager〔dryrunserver〕API端点
  启用了服务器端应用特性之后,PATCH服务端点接受额外的内容类型applicationapplypatchyaml。服务器端应用的用户就可以把YAMl格式的部分定义对象(partiallyspecifiedobjects)发送到此端点。当一个配置文件被应用时,它应该包含所有体现你意图的字段。清除ManagedFields
  可以从对象中剥离所有managedField,实现方法是通过使用MergePatch、StrategicMergePatch、JSONPatch、Update、以及所有的非应用方式的操作来覆盖它。这可以通过用空条目覆盖managedFields字段的方式实现。以下是两个示例:PATCHapiv1namespacesdefaultconfigmapsexamplecmContentType:applicationmergepatchjsonAccept:applicationjsonData:{metadata:{managedFields:〔{}〕}}PATCHapiv1namespacesdefaultconfigmapsexamplecmContentType:applicationjsonpatchjsonAccept:applicationjsonData:〔{op:replace,path:metadatamanagedFields,value:〔{}〕}〕
  这一操作将用只包含一个空条目的列表覆写managedFields,来实现从对象中整个的去除managedFields。注意,只把managedFields设置为空列表并不会重置字段。这么做是有目的的,所以managedFields将永远不会被与该字段无关的客户删除。
  在重置操作结合managedFields以外其他字段更改的场景中,将导致managedFields首先被重置,其他改变被押后处理。其结果是,应用者取得了同一个请求中所有字段的所有权。
  Caution:对于不接受资源对象类型的子资源(subresources),服务器端应用不能正确地跟踪其所有权。如果你对这样的子资源使用服务器端应用,变更的字段将不会被跟踪。
  参考链接:
  https:kubernetes。iozhdocsreferenceusingapiserversideapply
  顺便祝大家五一假期愉快,也不要忘记提升自己。
  欢迎大家提出不一样的观点,我们一起讨论,
  我是辣个男人,一个运维人。

我那灯塔般的家风(小学作文)在茫茫大海中,灯塔虽是那样渺小,但它的光芒却能穿透层层雾霭,找到迷路的小船。在我的生命中,也有那样一座灯塔,它引领我脱下迷惘,去寻找彼岸。它虽是灯塔,但它却是我们家族的信仰。这……那次我犯了错优秀作文在日常生活中,人们总会犯一些错。既然犯了错,我们就要及时改正。下面我们来看看《那次,我犯了错》。俗话说:金无足赤,人无完人。不管什么人,在日常生活中,或多或少都会犯一些或……微软宣布IE浏览器将于6月16日正式退役广州日报讯(全媒体记者许晓芳)5月16日,微软Edge浏览器通过其官方微博宣布,微软在1995年推出的IE浏览器,将于6月16日正式退役,之后其功能将由Edge浏览器接棒。……寻找记忆中的烟火作文街上空空荡荡地,孤零零的夜一如既往的平静,分明显出了寂寞的抱怨,这是除夕的夜晚。一点都没有从前那般热闹的感受,除了排列在路两旁整齐的红灯笼外,别无他物。我站在熟悉的窗口,寻找着……两个有关母爱的故事一个发生在一位游子与母亲之间。游子探亲期满离开故乡,母亲送他去车站,在车站,儿子旅行包的拎带突然被挤断。眼看就要到发车时间,母亲急忙从身上解下裤腰带,把儿子的旅行包扎好。儿子问……有启发的16个小故事(十)心中的顽石第十个故事:心中的顽石从前有一户人家的菜园摆着一颗大石头,宽度大约有四十公分,高度有十公分。到菜园的人,不小心就会踢到那一颗大石头,不是跌倒就是擦伤。儿子问:ldq……探险者与蝴蝶品学网专稿未经允许不得转载一天,一群探险者去荒岛上去冒险,路过一个山洞听到里头有些声音,队员们停下了脚步,队长说:ldquo;有事情了!rdquo;于是他们小心翼翼地往山……春天的花园作文400字第一篇:春天的花园我们学校有一座美丽的花园。花园位于学校大门的右恻,一进大门,便可以看见了。我们学校的花园一年四季都很美丽,但在这四个季节里我最喜欢的就是春天的小花……校庆日日记300字10月10日星期六晴晴天蓝蓝,白云朵朵,金色的太阳公公在空中微微笑,这个星期六举办热闹的校庆。全校师生以愉快的心情迎接今天,升旗时校长在台上致词,希望大家能玩得尽兴……那时花开作文700字碧绿色的叶在微光下摇曳,在琴音的氤氲中渐渐滋长出一帆旷世之花。十年,花开。红丝木的木板里,经时间的沉淀从木板夹缝中散发出岁月的味道,是檀香,是松樟,越经敲击,味道越……灰色时光小学叙事作文我不知道别人会不会也有一段时间心情极其抑郁。感觉处处灰色?但是,此时的我,心中却有着说不出的苦闷和伤痛,不知从何诉说,如何解脱阴天,坐在窗前,思绪空荡荡的,随手翻着网页,……那一次我真的哭了清风拂过,湖面好似一面平静,照出了我们丰富多彩的小学生活,这让我想起来了我们的毕业汇报演出,刚一开始,许多华丽的舞姿呈现在我们眼前,到了ldquo;感激启蒙老师rdquo;的环……
饿了么的配送是很坑骑手吗,你怎么看?非常坑,特别坑,超大坑,首先饿了么系统已经计算出你有可能超时了但是还会继续给你派单,接单后一直卡时间到距离超时还有20分钟开始打鸡血给你你派单,感觉像是强行加塞的,有时候……iphone8值不值得买?iPhone8已经上市,不过,对于绝大多数等待新iPhone的人来说,它似乎穿上了隐形披风,不仅在发布会上被iPhoneX完全抢掉了风头,就连开售之后,也只有尴尬的店门口的栅栏……关于双十一的作文双十一前几天正好是双十一,那一天,我十分生气,气得头发上都要着火了!刚开始,我也是无所谓的,双十一一般都是大人自己买想要的东西的日子,我也不知道为什么每年双十一的到来,那些大人……生与死作文400字从6岁起,我便知道,动植物有生必有死,尽管我改变不了这样的命运,却由衷的希望人能永远的生活在这个美好的世界上,也许,这个想法有点自私,可是,亲人大概就不会离开我了吧。我晚上一次……2017高考作文素材积累高考满分作文段落摘抄在高考的写作文的时候,有时一句优美的话就能为你锦上添花,如果你们写不出,可以参考下面是小编收集的2017高考作文素材积累:高考满分作文段落摘抄,希望对你们高考的写作有帮助!……比亚迪汉EV超长续航版豪华型,穿新衣咯!!自己有一台油车SUV,刚好有条件能拿车置换,于是想换车了,就是想图个省心、省油的车。研究了油电混、新能源一系列车,从最开始特斯拉、蔚来、理想研究中,突然比亚迪汉出现了!然后研究……请在意每个学生的进步E度网专稿未经允许不得转载王帆是初三班的优秀生,每次考过试,他总是以班级第一名的身份出现在那张红色的光荣榜上。所以他便理所应当的成为班主任王老师的得力助手和重点培育的对象……造物者真的存在吗(此图来源于网络)人类真的是演化而来的吗?还是有某种隐形的力量在控制我们?最初在我们地球出现了水,无机盐,蛋白质(氨基酸)。从而开始演变:无机小分子有机小分子有机大分子多……我家三口人的肩膀作文500字在社会生活中,每个人都有自己的肩膀,都肩负着自己的责任,我们家三口人的肩膀上也承担着各自的责任。爸爸是个电焊工,工作条件很艰苦,原来白白的脸庞,现在已经差不多和锅底一样黑……小狗乐乐优秀作文在我上一年级的时候,我们家迎来了一个新成员,它既不是弟弟,也不是妹妹,而是一只聪明而又可爱的小狗。我们给它起名叫乐乐,因为它给我们带来了快乐,而我们也希望它快乐的成长。它的毛是……拼多多入局同城配送?知情人士只是为了打通保供最后100米每经记者:李卓每经实习记者:杨昕怡每经编辑:刘雪梅4月20日每经快讯,据36氪报道,拼多多入局同城配送,现已向部分商家开放同城配送功能,商家只要拥有拼多多店铺和同城配送能……丢官日记丢官记换了新单位新领导不久,我就在换届选举中落选了,这对雄心勃勃欲更上一层楼的我来说,着实打击不小,郁闷了几天几夜,甚至在老妈面前伤心的满眼泪水,只是又硬忍了回去。唉,人……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网