k8s面试总结
master:etcd:保存集群所有的网络配置和资源对象的状态信息apierver:提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制contorll-manager:负责维护集群的状态,比如故障检测、自动扩展、滚动更新等scheuer:负责Node资源节点资源的调度node:docker:负责镜像管理和容器运行kubectl-proxy:为server代理和转发kub
1.简述一下k8s组件以及作用
master:
etcd:保存集群所有的网络配置和资源对象的状态信息
apierver:提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制
contorll-manager:负责维护集群的状态,比如故障检测、自动扩展、滚动更新等
scheuer:负责Node资源节点资源的调度
node:
docker:负责镜像管理和容器运行
kubectl-proxy:为server代理和转发
kubectl:维护容器的生命周期
2.简述pod创建过程
1.用户通过kubectl提交yaml配置文件
2.apiserver通过对应的kubeconfig进行认证,认证通过后将yaml中的pod信息存到etcd。
3.ControllerManager通过apiserver的watch接口发现了pod信息的更新,执行该资源所依赖的拓扑结构,
处理完将对应的信息交给apiserver,apiserver写到etcd。
4.Scheduler同样通过apiserver的watch接口更新到pod可以被调度,通过算法给pod分配节点,并将pod和对应节点
绑定的信息交给apiserver,.apiserver写到etcd。
5.kubelet从apiserver获取需要创建的pod信息,调用CNl接口给pod创建pod网络,调用CRI接口去启动容器,调用CSl
进行存储卷的挂载。
6.网络,容器,存储创建完成后pod创建完成,等业务进程启动后,pod运行成功。
2.kube-proxy作用?
kube-proxy 运行在所有节点上,它监听 apiserver 中 service 和 endpoint 的变化情况,创建路由规则以提供服务 IP 和负载均衡功能。简单理解此进程是Service的透明代理兼负载均衡器,其核心功能是将到某个Service的访问请求转发到后端的多个Pod实例上。
3.kube-proxy iptables原理?
通过API Server的Watch接口实时跟踪Service与Endpoint的变更信息,并更新对应的iptables规则,Client的请求流量则通过iptables的NAT机制“直接路由”到目标Pod。
4.kube-proxy ipvs原理?
IPVS则专门用于高性能负载均衡,并使用更高效的数据结构(Hash表),允许几乎无限的规模扩张,因此被kube-proxy采纳为最新模式。在IPVS模式下,使用iptables的扩展ipset,而不是直接调用iptables来生成规则链。iptables规则链是一个线性的数据结构,ipset则引入了带索引的数据结构,因此当规则很多时,也可以很高效地查找和匹配。可以将ipset简单理解为一个IP(段)的集合,这个集合的内容可以是IP地址、IP网段、端口等,iptables可以直接添加规则对这个“可变的集合”进行操作,这样做的好处在于可以大大减少iptables规则的数量,从而减少性能损耗
4.kube-proxy ipvs和iptables的异同?
iptables与IPVS都是基于Netfilter实现的,但因为定位不同,二者有着本质的差别:iptables是为防火墙而设计的;IPVS则专门用于高性能负载均衡,并使用更高效的数据结构(Hash表),允许几乎无限的规模扩张。
与iptables相比,IPVS拥有以下明显优势:
- 1、为大型集群提供了更好的可扩展性和性能;
- 2、支持比iptables更复杂的复制均衡算法(最小负载、最少连接、加权等);
- 3、支持服务器健康检查和连接重试等功能;
- 4、可以动态修改ipset的集合,即使iptables的规则正在使用这个集合。
5.pod运行状态
Pending:API Server已经创建该Pod,且Pod内还有一个或多个容器的镜像没有创建,包括正在下载镜像的过程。
Running:Pod内所有容器均已创建,且至少有一个容器处于运行状态、正在启动状态或正在重启状态。
Succeeded:Pod内所有容器均成功执行退出,且不会重启。
Failed:Pod内所有容器均已退出,但至少有一个容器退出为失败状态。
Unknown:由于某种原因无法获取该Pod状态,可能由于网络通信不畅导致。
6.创建过程
- 1、客户端提交Pod的配置信息(可以是yaml文件定义的信息)到kube-apiserver。
- 2、Apiserver收到指令后,通知给controller-manager创建一个资源对象。
- 3、Controller-manager通过api-server将pod的配置信息存储到ETCD数据中心中。
- 4、Kube-scheduler检测到pod信息会开始调度预选,会先过滤掉不符合Pod资源配置要求的节点,然后开始调度调优,主要是挑选出更适合运行pod的节点,然后将pod的资源配置单发送到node节点上的kubelet组件上。
- 5、Kubelet根据scheduler发来的资源配置单运行pod,运行成功后,将pod的运行信息返回给scheduler,scheduler将返回的pod运行状况的信息存储到etcd数据中心。
6.2删除过程:
1. 用户发出删除 pod 命令 2. Pod 对象随着时间的推移更新,在宽限期(默认情况下30秒),pod 被视为“dead”状态 3. 将 pod 标记为“Terminating”状态 4. 第三步同时运行,监控到 pod 对象为“Terminating”状态的同时启动 pod 关闭过程 5.第三步同时进行,endpoints 控制器监控到 pod 对象关闭,将pod与service匹配的 endpoints 列表中删除 6. 如果 pod 中定义了 preStop 钩子处理程序,则 pod 被标记为“Terminating”状态时以同步的方式启动执行;若宽限期结束后,preStop 仍未执行结束,第二步会重新执行并额外获得一个2秒的小宽限期 7. Pod 内对象的容器收到 TERM 信号 8. 宽限期结束之后,若存在任何一个运行的进程,pod 会收到 SIGKILL 信号 9. Kubelet 请求 API Server 将此 Pod 资源宽限期设置为0从而完成删除操作
6.Kubernetes如何进行优雅的节点关机维护?
由于Kubernetes节点运行大量Pod,因此在进行关机维护之前,建议先使用kubectl drain将该节点的Pod进行驱逐,然后进行关机维护。
7.如何使用EFK实现日志的统一管理?
- Elasticsearch:是一个搜索引擎,负责存储日志并提供查询接口;
- Fluentd:负责从 Kubernetes 搜集日志,每个node节点上面的fluentd监控并收集该节点上面的系统日志,并将处理过后的日志信息发送给Elasticsearch;
- Kibana:提供了一个 Web GUI,用户可以浏览和搜索存储在 Elasticsearch 中的日志。 通过在每台node上部署一个以DaemonSet方式运行的fluentd来收集每台node上的日志。Fluentd将docker日志目录/var/lib/docker/containers和/var/log目录挂载到Pod中,然后Pod会在node节点的/var/log/pods目录中创建新的目录,可以区别不同的容器日志输出,该目录下有一个日志文件链接到/var/lib/docker/contianers目录下的容器日志输出。
8.Kubernetes Metric Service?
采用Metrics Server作为默认的性能数据采集和监控,主要用于提供核心指标(Core Metrics),包括Node、Pod的CPU和内存使用指标。
对其他自定义指标(Custom Metrics)的监控则由Prometheus等组件来完成。
9.Kubernetes中Pod的重启策略?
- Always:当容器失效时,由kubelet自动重启该容器;
- OnFailure:当容器终止运行且退出码不为0时,由kubelet自动重启该容器;
- Never:不论容器运行状态如何,kubelet都不会重启该容器。
10.Kubernetes中Pod的健康
ExecAction:在容器中执行命令,根据返回的状态码判断容器健康状态, 返回0即表示成功,否则为失败。
TCPSocketAction: 通过与容器的某TCP端口尝试建立连接进行诊断, 端口能打开即为表示成功,否则失败。
HTTPGetAction:向容器指定 URL 发起 HTTP GET 请求, 响应码为2xx或者是3xx为成功,否则失败。
LivenessProbe探针:用于判断容器是否存活(running状态),如果LivenessProbe探针探测到容器不健康,则kubelet将杀掉该容器,并根据容器的重启策略做相应处理。若一个容器不包含LivenessProbe探针,kubelet认为该容器的LivenessProbe探针返回值用于是“Success”。
ReadineeProbe探针:用于判断容器是否启动完成(ready状态)。如果ReadinessProbe探针探测到失败,则Pod的状态将被修改。Endpoint Controller将从Service的Endpoint中删除包含该容器所在Pod的Eenpoint。
startupProbe探针:启动检查机制,应用一些启动缓慢的业务,避免业务长时间启动而被上面两类探针kill掉。startupProbe探测成功后再交给livenessProbe。咱们startupProbe配置的是10*10s,
也就是说只要应用在100s内启动都是OK的,而且应用挂掉了10s就会发现问题
11.Pod的常见调度方式?
- Deployment或RC:该调度策略主要功能就是自动部署一个容器应用的多份副本,以及持续监控副本的数量,在集群内始终维持用户指定的副本数量。
- NodeSelector:定向调度,当需要手动指定将Pod调度到特定Node上,可以通过Node的标签(Label)和Pod的nodeSelector属性相匹配。
- NodeAffinity亲和性调度:亲和性调度机制极大的扩展了Pod的调度能力,目前有两种节点亲和力表达:
- requiredDuringSchedulingIgnoredDuringExecution:硬规则,必须满足指定的规则,调度器才可以调度Pod至Node上(类似nodeSelector,语法不同)。
- preferredDuringSchedulingIgnoredDuringExecution:软规则,优先调度至满足的Node的节点,但不强求,多个优先级规则还可以设置权重值。
- Taints和Tolerations(污点和容忍):
- Taint:使Node拒绝特定Pod运行;
- Toleration:为Pod的属性,表示Pod能容忍(运行)标注了Taint的Node。
12.init container pasue
Init 容器是一种特殊容器,在 Pod 内的应用容器启动之前运行。Init 容器可以包括一些应用镜像中不存在的实用工具和安装脚本。
Init 容器与普通的容器非常像,除了如下两点:
- 它们总是运行到完成。
- 每个都必须在下一个启动之前成功完成。 如果 Pod 的 Init 容器失败,kubelet 会不断地重启该 Init 容器直到该容器成功为止pause容器主要为每个业务容器提供以下功能: PID命名空间:Pod中的不同应用程序可以看到其他应用程序的进程ID。 网络命名空间:Pod中的多个容器能够访问同一个IP和端口范围。 IPC命名空间:Pod中的多个容器能够使用SystemV IPC或POSIX消息队列进行通信。 UTS命名空间:Pod中的多个容器共享一个主机名;Volumes(共享存储卷) pause container作为pod里其他所有container的parent container,主要有两个职责: 是pod里其他容器共享Linux namespace的基础 扮演PID 1的角色,负责处理僵尸进程
13.deployment升级过程?
- 初始创建Deployment时,系统创建了一个ReplicaSet,并按用户的需求创建了对应数量的Pod副本。
- 当更新Deployment时,系统创建了一个新的ReplicaSet,并将其副本数量扩展到1,然后将旧ReplicaSet缩减为2。
- 之后,系统继续按照相同的更新策略对新旧两个ReplicaSet进行逐个调整。
- 最后,新的ReplicaSet运行了对应个新版本Pod副本,旧的ReplicaSet副本数量则缩减为0。
14.deployment升级策略?
Recreate:设置spec.strategy.type=Recreate,表示Deployment在更新Pod时,会先杀掉所有正在运行的Pod,然后创建新的Pod。
RollingUpdate:设置spec.strategy.type=RollingUpdate,表示Deployment会以滚动更新的方式来逐个更新Pod。同时,可以通过设置spec.strategy.rollingUpdate下的两个参数(maxUnavailable和maxSurge)来控制滚动更新的过程。
15.DaemonSet类型的资源特性?
DaemonSet资源对象会在每个Kubernetes集群中的节点上运行,并且每个节点只能运行一个pod,这是它和deployment资源对象的最大也是唯一的区别。因此,在定义yaml文件中,不支持定义replicas。
它的一般使用场景如下:
- 在去做每个节点的日志收集工作。
- 监控每个节点的的运行状态。
16.service三种工作模式
第一种: 是Userspace方式
Client Pod要访问Server Pod时,它先将请求发给本机内核空间中的service规则,由它再将请求,转给监听在指定套接字上的kube-proxy,kube-proxy处理完请求,并分发请求到指定Server Pod后,再将请求递交给内核空间中的service,由service将请求转给指定的Server Pod。
第二种: iptables模型
此工作方式是直接由内核中的iptables规则,接受Client Pod的请求,并处理完成后,直接转发给指定ServerPod
第三种: ipvs模型
它是直接有内核中的ipvs规则来接受Client Pod请求,并处理该请求,再有内核封包后,直接发给指定的Server Pod。
17.service的四种类型
ClusterIp :是 Kubernetes 中默认的服务类型 (ServiceType),选择此种类型,对应的 Service 将被分配一个集群内部的 IP 地址,只能在集群内部被访问
nodePort :在每台 Node 的固定端口上暴露服务,选择 NodePort 的服务类型,集群会自动创建一个 ClusterIp 类型的服务,负责处理Node接收到的外部流量。集群外部的 Client 可以通过:的方式访问该服务。
LoadBalancer :通过集群外部的负载均衡设备来暴露服务,负载均衡设备一般由云厂商提供或者使用者自行搭建,在 UK8S中,我们通过Cloud Controller Manager插件集成了 ULB,后面会有专门的介绍。需要注意的是,创建一个 LoadBalancer 的 Service,集群会自动创建一个 NodePort 和 ClusterIp 类型的 Service,用于接收从 Ulb 接入的流量。
ExternalName : 将服务映射到一个 DNS 域名(如example.test.ucloud.cn),DNS 域名可通spec.externalName 参数配置。跨集群访问
pod跨名称空间namespace与Service通信
说明:是通过Service的NAME进行通信,而不是Service的IP【因为每次重启Service,NAME不会改变,而IP是会改变的】。
通过Service的ExternalName类型即可实现跨namespace名称空间与Service通信
Service域名格式:$(service name).$(namespace).svc.cluster.local,其中 cluster.local 为指定的集群的域名
18.pod网络模式
hostNetwork 主机网络模式 ,相当于 docker run --net=host
hostPort。hostPort是直接将容器的端口与所调度的节点上的端口路由
NodePort 模式 NodePort 模式 k8s service 默认情况使用 cluster IP 的方式,
19.Service分发后端的策略?
- RoundRobin:默认为轮询模式,即轮询将请求转发到后端的各个Pod上。
- SessionAffinity:基于客户端IP地址进行会话保持的模式,即第1次将某个客户端发起的请求转发到后端的某个Pod上,之后从相同的客户端发起的请求都将被转发到后端相同的Pod上。
20.ingress?
Kubernetes的Ingress资源对象,用于将不同URL的访问请求转发到不同的Service,以实现HTTP层的业务路由机制。
Kubernetes使用了Ingress策略和Ingress Controller,两者结合并实现了一个完整的Ingress负载均衡器。使用Ingress进行负载分发时,Ingress Controller基于Ingress规则将客户端请求直接转发到Service对应的后端Endpoint(Pod)上,从而跳过kube-proxy的转发功能,kube-proxy不再起作用,全过程为:ingress controller + ingress 规则 ----> services。
同时当Ingress Controller提供的是对外服务,则实际上实现的是边缘路由器的功能。
21.镜像的下载策略?
- Always:镜像标签为latest时,总是从指定的仓库中获取镜像。
- Never:禁止从仓库中下载镜像,也就是说只能使用本地镜像。
- IfNotPresent:仅当本地没有对应镜像时,才从目标仓库中下载。默认的镜像下载策略是:当镜像标签是latest时,默认策略是Always;当镜像标签是自定义时(也就是标签不是latest),那么默认策略是IfNotPresent。
22.Scheduler使用哪两种算法将Pod绑定到worker节点?
预选(Predicates):输入是所有节点,输出是满足预选条件的节点。kube-scheduler根据预选策略过滤掉不满足策略的Nodes。如果某节点的资源不足或者不满足预选策略的条件则无法通过预选。如“Node的label必须与Pod的Selector一致”。
优选(Priorities):输入是预选阶段筛选出的节点,优选会根据优先策略为通过预选的Nodes进行打分排名,选择得分最高的Node。例如,资源越富裕、负载越小的Node可能具有越高的排名。
23.kubelet监控Worker节点资源是使用什么组件来实现的?
kubelet使用cAdvisor对worker节点资源进行监控。在 Kubernetes 系统中,cAdvisor 已被默认集成到 kubelet 组件内,当 kubelet 服务启动时,它会自动启动 cAdvisor 服务,然后 cAdvisor 会实时采集所在节点的性能指标及在节点上运行的容器的性能指标。
24.如何保证集群的安全性?
- 基础设施方面:保证容器与其所在宿主机的隔离;
- 权限方面:
- 最小权限原则:合理限制所有组件的权限,确保组件只执行它被授权的行为,通过限制单个组件的能力来限制它的权限范围。
- 用户权限:划分普通用户和管理员的角色。
- 集群方面:
- API Server的认证授权:Kubernetes集群中所有资源的访问和变更都是通过Kubernetes API Server来实现的,因此需要建议采用更安全的HTTPS或Token来识别和认证客户端身份(Authentication),以及随后访问权限的授权(Authorization)环节。
- API Server的授权管理:通过授权策略来决定一个API调用是否合法。对合法用户进行授权并且随后在用户访问时进行鉴权,建议采用更安全的RBAC方式来提升集群安全授权。
- 敏感数据引入Secret机制:对于集群敏感数据建议使用Secret方式进行保护。
- AdmissionControl(准入机制):对kubernetes api的请求过程中,顺序为:先经过认证 & 授权,然后执行准入操作,最后对目标对象进行操作。
25.RBAC及其特点(优势)?
RBAC是基于角色的访问控制,是一种基于个人用户的角色来管理对计算机或网络资源的访问的方法。
相对于其他授权模式,RBAC具有如下优势:
- 对集群中的资源和非资源权限均有完整的覆盖。
- 整个RBAC完全由几个API对象完成, 同其他API对象一样, 可以用kubectl或API进行操作。
- 可以在运行时进行调整,无须重新启动API Server。
26. Secret作用?
Secret对象,主要作用是保管私密数据,比如密码、OAuth Tokens、SSH Keys等信息。将这些私密信息放在Secret对象中比直接放在Pod或Docker Image中更安全,也更便于使用和分发。
- 在创建Pod时,通过为Pod指定Service Account来自动使用该Secret。
- 通过挂载该Secret到Pod来使用它。
- 在Docker镜像下载时使用,通过指定Pod的spc.ImagePullSecrets来引用它
27.flannel的作用?
- 它能协助Kubernetes,给每一个Node上的Docker容器都分配互相不冲突的IP地址。
- 它能在这些IP地址之间建立一个覆盖网络(Overlay Network),通过这个覆盖网络,将数据包原封不动地传递到目标容器内。
28. Calico网络组件实现原理? kalilkou
Calico是一个基于BGP的纯三层的网络方案,与OpenStack、Kubernetes、AWS、GCE等云平台都能够良好地集成。
Calico在每个计算节点都利用Linux Kernel实现了一个高效的vRouter来负责数据转发。每个vRouter都通过BGP协议把在本节点上运行的容器的路由信息向整个Calico网络广播,并自动设置到达其他节点的路由转发规则。
Calico保证所有容器之间的数据流量都是通过IP路由的方式完成互联互通的。Calico节点组网时可以直接利用数据中心的网络结构(L2或者L3),不需要额外的NAT、隧道或者Overlay Network,没有额外的封包解包,能够节约CPU运算,提高网络效率。
calico根据iptables规则进行路由转发,并没有进行封包,解包的过程,这和flannel比起来效率就会快多
Flannel》也就是将TCP数据包装在另一种网络包里面进行路由转发和通信,目前已经支持UDP、VxLAN、AWS VPC和GCE路由等数据转发方式。
默认的节点间数据通信方式是UDP转发。
29.数据持久化的方式有哪些?
Kubernetes 通过数据持久化来持久化保存重要数据,常见的方式有:
EmptyDir(空目录):没有指定要挂载宿主机上的某个目录,直接由Pod内保部映射到宿主机上。类似于docker中的manager volume。
- 场景:
- 只需要临时将数据保存在磁盘上,比如在合并/排序算法中;
- 作为两个容器的共享存储。
- 特性:
- 同个pod里面的不同容器,共享同一个持久化目录,当pod节点删除时,volume的数据也会被删除。
- emptyDir的数据持久化的生命周期和使用的pod一致,一般是作为临时存储使用。Hostpath:将宿主机上已存在的目录或文件挂载到容器内部。类似于docker中的bind mount挂载方式。
- 特性:增加了pod与节点之间的耦合。PersistentVolume(简称PV):如基于NFS服务的PV,也可以基于GFS的PV。它的作用是统一数据持久化目录,方便管理。
30.简述Kubernetes PV和PVC?
PV是对底层网络共享存储的抽象,将共享存储定义为一种“资源”。
PVC则是用户对存储资源的一个“申请”。
31.简述Kubernetes PV生命周期内的阶段?
某个PV在生命周期中可能处于以下4个阶段(Phaes)之一。
- Available:可用状态,还未与某个PVC绑定。
- Bound:已与某个PVC绑定。
- Released:绑定的PVC已经删除,资源已释放,但没有被集群回收。
- Failed:自动资源回收失败。
32.简述Kubernetes所支持的存储供应模式?
Kubernetes支持两种资源的存储供应模式:静态模式(Static)和动态模式(Dynamic)。
静态模式:集群管理员手工创建许多PV,在定义PV时需要将后端存储的特性进行设置。
动态模式:集群管理员无须手工创建PV,而是通过StorageClass的设置对后端存储进行描述,标记为某种类型。此时要求PVC对存储的类型进行声明,系统将自动完成PV的创建及与PVC的绑定。
33.简述Kubernetes CSI模型?
Kubernetes CSI是Kubernetes推出与容器对接的存储接口标准,存储提供方只需要基于标准接口进行存储插件的实现,就能使用Kubernetes的原生存储机制为容器提供存储服务。CSI使得存储提供方的代码能和Kubernetes代码彻底解耦,部署也与Kubernetes核心组件分离,显然,存储插件的开发由提供方自行维护,就能为Kubernetes用户提供更多的存储功能,也更加安全可靠。
CSI包括CSI Controller和CSI Node:
- CSI Controller的主要功能是提供存储服务视角对存储资源和存储卷进行管理和操作。
- CSI Node的主要功能是对主机(Node)上的Volume进行管理和操作。
34.简述Kubernetes Worker节点加入集群的过程?
通常需要对Worker节点进行扩容,从而将应用系统进行水平扩展。主要过程如下:
- 1、在该Node上安装Docker、kubelet和kube-proxy服务;
- 2、然后配置kubelet和kubeproxy的启动参数,将Master URL指定为当前Kubernetes集群Master的地址,最后启动这些服务;
- 3、通过kubelet默认的自动注册机制,新的Worker将会自动加入现有的Kubernetes集群中;
- 4、Kubernetes Master在接受了新Worker的注册之后,会自动将其纳入当前集群的调度范围。
35.简述Kubernetes Pod如何实现对节点的资源控制?
Kubernetes集群里的节点提供的资源主要是计算资源,计算资源是可计量的能被申请、分配和使用的基础资源。当前Kubernetes集群中的计算资源主要包括CPU、GPU及Memory。CPU与Memory是被Pod使用的,因此在配置Pod时可以通过参数CPU Request及Memory Request为其中的每个容器指定所需使用的CPU与Memory量,Kubernetes会根据Request的值去查找有足够资源的Node来调度此Pod。
通常,一个程序所使用的CPU与Memory是一个动态的量,确切地说,是一个范围,跟它的负载密切相关:负载增加时,CPU和Memory的使用量也会增加。
35.简述Kubernetes Requests和Limits如何影响Pod的调度?
当一个Pod创建成功时,Kubernetes调度器(Scheduler)会为该Pod选择一个节点来执行。对于每种计算资源(CPU和Memory)而言,每个节点都有一个能用于运行Pod的最大容量值。调度器在调度时,首先要确保调度后该节点上所有Pod的CPU和内存的Requests总和,不超过该节点能提供给Pod使用的CPU和Memory的最大容量值。
request(资源需求):即运行Pod的节点必须满足运行Pod的最基本需求才能 运行Pod。
• limit(资源限额):即运行Pod期间,可能内存使用量会增加,那最多能使用多少内存,这就是资源限额。
资源限制:
LimitRange做一个全局限制。如果在部署Pod的时候指定了requests和Limits,则指定的生效。反之则由全局的给Pod加上默认的限制。 LimitRange可以用来限制Pod,也可以限制Container。
总结,LimitRange可以实现的功能:
- 限制namespace中每个pod或container的最小和最大资源用量。
- 限制namespace中每个PVC的资源请求范围。
- 限制namespace中资源请求和限制数量的比例。
- 配置资源的默认限制。
更多推荐
所有评论(0)