首页 文章 精选 留言 我的

精选列表

搜索[AI原生],共10000篇文章
优秀的个人博客,低调大师

原生Kubernetes监控功能详解-Part2

今晚20:30,Kubernetes Master Class在线培训第六期《在Kubernetes中创建高可用应用》即将开播,点击http://live.vhall.com/847710932 即可免费预约注册! 监控的重要性不言而喻,它让我们能充分了解到应用程序的状况。Kubernetes有很多内置工具可供用户们选择,让大家更好地对基础架构层(node)和逻辑层(pod)有充分的了解。 在本系列文章的第一篇中,我们关注了专为用户提供监控和指标的工具Dashboard和cAdvisor。在本文中,我们将继续分享关注工作负载扩缩容和生命周期管理的监控工具:Probe(探针)和Horizontal Pod Autoscaler(HPA)。同样的,一切介绍都将以demo形式进行。 Demo的前期准备 在本系列文章的上一篇中,我们已经演示了如何启动Rancher实例以及Kubernetes集群。在上篇文章中我们提到过,Kubernetes附带了一些内置的监控工具,包括: Kubernetes dashboard:为集群上运行的资源提供一个概览。它还提供了一种非常基本的部署以及与这些资源进行交互的方法。 cAdvisor:一种用于监控资源使用情况并分析容器性能的开源代理。 Liveness和Readiness Probe:主动监控容器的健康情况。 Horizontal Pod Autoscaler:基于通过分析不同指标所收集的信息,根据需要增加pod的数量。 在上篇文章了,我们深度分析了前两个组件Kubernetes Dashboard和cAdvisor,在本文中,我们将继续探讨后两个工具:探针及HPA。 Probe(探针) 健康检查有两种:liveness和readiness。 readiness探针让Kubernetes知道应用程序是否已准备好,来为流量提供服务。只有探针允许通过时,Kubernetes才会允许服务将流量发送到pod。如果探针没有通过,Kubernetes将停止向该Pod发送流量,直到再次通过为止。 当你的应用程序需要花费相当长的时间来启动时,readiness探针非常有用。即使进程已经启动,在探针成功通过之前,该服务也无法工作。默认情况下,Kubernetes将在容器内的进程启动后立即开始发送流量,但是在有readiness探针的情况下,Kubernetes将在应用程序完全启动后再允许服务路由流量。 liveness探针让Kubernetes知道应用程序是否处于运行状态。如果处于运行状态,则不采取任何行动。如果该应用程序未处于运行状态,Kubernetes将删除该pod并启动一个新的pod替换之前的pod。当你的应用程序停止提供请求时,liveness探针非常有用。由于进程仍在运行,因此默认情况下,Kubernetes将继续向pod发送请求。凭借liveness探针,Kubernetes将检测到应用程序不再提供请求并将重新启动pod。 对于liveness和readiness检查,可以使用以下类型的探针: http:自定义探针中最常见的一种。Kubernetes ping一条路径,如果它在200-300的范围内获得http响应,则将该pod标记为健康。 command:使用此探针时,Kubernetes将在其中一个pod容器内运行命令。如果该命令返回退出代码0,则容器将标记为健康。 tcp:Kubernetes将尝试在指定端口上建立TCP连接。如果能够建立连接,则容器标记为健康。 配置探针时,可提供以下参数: initialDelaySeconds:首次启动容器时,发送readiness/liveness探针之前等待的时间。对于liveness检查,请确保仅在应用程序准备就绪后启动探针,否则你的应用程序将会继续重新启动。 periodSeconds:执行探针的频率(默认值为10)。 timeoutSeconds:探针超时的时间,以秒为单位(默认值为1)。 successThreshold:探针成功的最小连续成功检查次数。 failureThreshold:放弃之前探针失败的次数。放弃liveness探针会导致Kubernetes重新启动pod。对于liveness探针,pod将被标记为未准备好。 Readiness探针的演示 在本节中,我们将使用命令检查来配置readiness探针。我们将使用默认的nginx容器部署两个副本。在容器中找到名为/tmp/healthy的文件之前,不会有任何流量发送到pod。 首先,输入以下命令创建一个readiness.yaml文件: apiVersion: apps/v1 kind: Deployment metadata: name: readiness-demo spec: selector: matchLabels: app: nginx replicas: 2 template: metadata: labels: app: nginx spec: containers: - image: nginx name: nginx ports: - containerPort: 80 readinessProbe: exec: command: - ls - /tmp/healthy initialDelaySeconds: 5 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: lb spec: type: LoadBalancer ports: - port: 80 protocol: TCP targetPort: 80 selector: > apiVersion: apps/v1 kind: Deployment metadata: name: readiness-demo spec: selector: matchLabels: app: nginx replicas: 2 template: metadata: labels: app: nginx spec: containers: - image: nginx name: nginx ports: - containerPort: 80 readinessProbe: exec: command: - ls - /tmp/healthy initialDelaySeconds: 5 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: lb spec: type: LoadBalancer ports: - port: 80 protocol: TCP targetPort: 80 selector: app: nginx app: nginx 接下来,应用YAML文件: 我们将看到正在创建的部署和服务: 除非readiness探针通过,否则pod将不会进入READY状态。在这种情况下,由于没有名为/tmp/healthy的文件,因此将被标记为失败,服务将不会发送任何流量。 为了更好地理解,我们将修改两个pod的默认nginx索引页面。当被请求时,第一个将显示1作为响应,第二个将显示2作为响应。 下面将特定pod名称替换为计算机上部署创建的pod名称: 在第一个pod中创建所需的文件,以便转换到READY状态并可以在那里路由流量: 探针每5秒运行一次,因此我们可能需要稍等一会儿才能看到结果: 一旦状态发生变化,我们就可以开始点击负载均衡器的外部IP: 下面我们应该能看到我们修改过的Nginx页面了,它由一个数字标识符组成: 为第二个pod创建文件也会导致该pod进入READY状态。此处的流量也会被重定向: 当第二个pod标记为READY时,该服务将向两个pod发送流量: 此时的输出应该已经指明了,流量正在两个pod之间分配: Liveness探针的演示 在本节中,我们将演示使用tcp检查配置的liveness探针。如上所述,我们将使用默认的nginx容器部署两个副本。如果容器内的端口80没有正处于监听状态,则不会将流量发送到容器,并且将重新启动容器。 首先,我们来看看liveness探针演示文件: apiVersion: apps/v1 kind: Deployment metadata: name: liveness-demo spec: selector: matchLabels: app: nginx replicas: 2 template: metadata: labels: app: nginx spec: containers: - image: nginx name: nginx ports: - containerPort: 80 livenessProbe: tcpSocket: port: 80 initialDelaySeconds: 15 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: lb spec: type: LoadBalancer ports: - port: 80 protocol: TCP targetPort: 80 selector: app: nginx 我们可以用一个命令来应用YAML: 然后,我们可以检查pod,并像上面一样修改默认的Nginx页面以使用简单的1或2来表示响应。 首先,找到Nginx部署给pod的名称: 接下来,使用数字标识符替换每个pod中的默认索引页: 流量已被服务重定向,因此可立即从两个pod获取响应: 同样,响应应该表明流量正在两个pod之间分配: 现在我们已经准备好在第一个pod中停止Nginx进程,以查看处于运行状态的liveness探针。一旦Kubernetes注意到容器不再监听端口80,pod的状态将会改变并重新启动。我们可以观察其转换的一些状态,直到再次正常运行。 首先,停止其中一个pod中的Web服务器进程: 现在,当Kubernetes注意到探针失败并采取措施重启pod时,审核pod的状态: 你可能会看到pod在再次处于健康状况之前进行了多种状态的转换: 如果我们通过我们的服务请求页面,我们将从第二个pod中看到正确的响应,即修改后的标识符“2”。然而,刚创建的pod将从容器镜像返回了默认的Nginx页面: 这表明Kubernetes已经部署了一个全新的pod来替换之前失败的pod。 Horizontal Pod Autoscaler Horizontal Pod Autoscaler(HPA)是Kubernetes的一项功能,使我们能够根据观察到的指标对部署、复制控制器、或副本集所需的pod数量进行自动扩缩容。在实际使用中,CPU指标通常是最主要的触发因素,但自定义指标也可以是触发因素。 基于测量到的资源使用情况,该过程的每个部分都是自动完成的,不需要人工干预。相关指标可以从metrics.k8s.io、custom.metrics.k8s.io或external.metrics.k8s.io等API获取。 在下文的示例中,我们将基于CPU指标进行演示。我们可以在这种情况下使用的命令是kubectl top pods,它将显示pod的CPU和内存使用情况。 首先,创建一个YAML文件,该文件将使用单个副本创建部署: 输入以下内容来应用部署: 这是一个很简单的deployment,具有相同的Nginx镜像和单个副本: 接下来,让我们了解如何实现自动缩放机制。使用kubectl get / describe hpa命令,可以查看当前已定义的autoscaler。要定义新的autoscaler,我们可以使用kubectl create命令。但是,创建autoscaler的最简单方法是以现有部署为目标,如下所示: 这将为我们之前创建的hpa-demo部署创建一个autoscaler,而且预计CPU利用率为50%。副本号在此处设为1到10之间的数字,因此当高负载时autoscaler将创建的最大pod数为10。 你可以通过输入以下内容来确认autoscaler的配置: 我们也可以用YAML格式定义它,从而更容易地进行审查和变更管理: 为了查看HPA的运行情况,我们需要运行一个在CPU上创建负载的命令。这里有很多种方法,但一个非常简单的例子如下: 首先,检查唯一pod上的负载。因为它目前处于空闲状态,所以没有太多负载: 现在,让我们在当前pod上生成一些负载。一旦负载增加,我们应该就能看到HPA开始自动创建一些额外的pod来处理所增加的负载。让以下命令运行几秒钟,然后停止命令: 检查当前pod上的当前负载: HPA开始发挥作用,并开始创建额外的pod。Kubernetes显示部署已自动缩放,现在有三个副本: 我们可以看到HPA的详细信息以及将其扩展到三个副本的原因: Name: hpa-demo Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"autoscaling/v1","kind":"HorizontalPodAutoscaler","metadata":{"annotations":{},"name":"hpa-demo","namespace":"default"},"spec":{"maxRepli... CreationTimestamp: Sat, 30 Mar 2019 17:43:50 +0200 Reference: Deployment/hpa-demo Metrics: ( current / target ) resource cpu on pods (as a percentage of request): 104% (104m) / 50% Min replicas: 1 Max replicas: 10 Conditions: Type Status Reason Message ---- ------ ------ ------- AbleToScale True ReadyForNewScale recommended size matches current size ScalingActive True ValidMetricFound the HPA was able to successfully calculate a replica count from cpu resource utilization (percentage of request) ScalingLimited False DesiredWithinRange the desired count is within the acceptable range Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulRescale 15s horizontal-pod-autoscaler New size: 3; reason: cpu resource utilization (percentage of request) above target 由于我们停止了生成负载的命令,所以如果我们等待几分钟,HPA应该注意到负载减少并缩小副本的数量。没有高负载,就不需要创建额外的两个pod。 autoscaler在Kubernetes中执行缩减操作之前等待的默认时间是5分钟。您可以通过调整--horizontal-pod-autoscaler-downscale-delay设置来修改该时间,更多信息可以参考官方文档: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ 等待时间结束后,我们会发现,在高负载的标记处,部署的pod数量减少了: pod数量会变回到基数: 如果再次检查HPA的描述,我们将能看到减少副本数量的原因: Name: hpa-demo Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"autoscaling/v1","kind":"HorizontalPodAutoscaler","metadata":{"annotations":{},"name":"hpa-demo","namespace":"default"},"spec":{"maxRepli... CreationTimestamp: Sat, 30 Mar 2019 17:43:50 +0200 Reference: Deployment/hpa-demo Metrics: ( current / target ) resource cpu on pods (as a percentage of request): 0% (0) / 50% Min replicas: 1 Max replicas: 10 Conditions: Type Status Reason Message ---- ------ ------ ------- AbleToScale True SucceededRescale the HPA controller was able to update the target scale to 1 ScalingActive True ValidMetricFound the HPA was able to successfully calculate a replica count from cpu resource utilization (percentage of request) ScalingLimited True TooFewReplicas the desired replica count is increasing faster than the maximum scale rate Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulRescale 5m horizontal-pod-autoscaler New size: 3; reason: cpu resource utilization (percentage of request) above target Normal SuccessfulRescale 13s horizontal-pod-autoscaler New size: 1; reason: All metrics below target` 结 语 相信通过这两篇系列文章,我们能够深入地理解了Kubernetes是如何使用内置工具为集群设置监控的。我们知道了Kubernetes在幕后如何通过不间断的工作来保证应用程序的运行,同时可以的话也应该更进一步去了解其背后的原理。 从Dashboard和探针收集所有数据,再通过cAdvisor公开所有容器资源,可以帮助我们了解到资源限制或容量规划。良好地监控Kubernetes是至关重要的,因为它可以帮助我们了解到集群、以及在集群上运行着的应用程序的运行状况和性能。

优秀的个人博客,低调大师

原生Kubernetes监控功能详解-Part1

Kubernetes是一个开源的容器编排框架,它为我们提供了一种简单的部署、扩展和监控的方法。本文将讨论Kubernetes的内置监控功能,如Kubernetes dashboard、cAdvisor等。为便于更好地理解,文内还提供了演示。 介 绍 Kubernetes是一个开源的容器编排框架,它为我们提供了一种简单的部署、扩展和监控的方法。在本文中,我们将讨论Kubernetes的内置监控功能。为了便于读者更好地理解,本文包含了一些演示。 Kubernetes架构概述 在基础架构级别,Kubernetes集群是一组各自发挥特定功能的物理机或虚拟机。充当主要角色的物理机或虚拟机负责整个操作,并协调在所有node上运行的容器管理。 Master组件管理pod的生命周期: apiserver:为所有其他master组件公开API的主组件 scheduler:负责依照pod规范中的信息来决定pod应该运行在哪个node上 controller-manager:负责node管理(检测node是否出现故障)、pod复制和endpoint创建 etcd:用于存储所有内部集群数据的键/值存储 Node组件是Kubernetes中由master管理的worker机器。每个node都包含运行pod所需的必要组件: kubelet:处理master及其上运行的node之间的所有通信。它与容器运行时配合,负责部署和监控容器 kube-proxy:负责维护node的网络规则,还负责处理pod、node和外部之间的通信。 容器运行时:在node上运行容器。 从逻辑角度看,一个Kubernetes部署,是由在集群中各自发挥作用的各个组件组成: Pod:Kubernetes内部的基本部署单位。一个pod由一个或多个容器组成,这些容器共享网络命名空间和IP地址。 Service:充当负载均衡器。它们在池(一组pod)之前提供IP地址,且还提供控制访问IP地址的策略。 ReplicaSet:由deployment控制,负责确保deployment所需数量的pod都正常运行。 Namespace(命名空间):为pod或service等不同类型的资源定义逻辑隔离。 Metadata:根据容器的部署特征对容器进行标记。 监控Kubernetes 若我们想要预测问题并发现开发或部署中潜在的瓶颈,那么对应用程序进行监控是必不可少的。 为了帮助监控集群和构成部署的许多活动组件,Kubernetes提供了一些内置的监控功能: Kubernetes dashboard:为集群上运行的资源提供一个概览。它还提供了一种非常基本的部署以及与这些资源进行交互的方法。 cAdvisor:一种用于监控资源使用情况并分析容器性能的开源代理。 Liveness和Readiness Probe:主动监控容器的健康情况。 Horizontal Pod Autoscaler:基于通过分析不同指标所收集的信息,根据需要增加pod的数量。 在本文中,我们将重点介绍前两个内置工具。在本系列文章的下一篇中,我们将介绍其他的监控工具。 Kubernetes中有许多指标需要监控。正如我们会以两种不同的方式(基础架构和逻辑)描述架构那样,我们也可以将监控分为两个主要组件:监控集群本身以及集群上运行的工作负载监控。 集群监控 所有集群都应监控底层服务器组件,因为服务器层的问题往往都会出现在工作负载中。监控node资源时要注意的一些指标包括CPU、磁盘和网络带宽。了解这些指标可以让我们知道是否需要对集群进行扩容或缩容(如果企业使用的是云提供商,对运行成本很看重,那么这一点更尤其重要)。 工作负载监控 我们还需要考虑与部署及其pod相关的指标。其中重要的一点,是将deployment中当下运行的pod数量与期望的数量进行对比。此外,我们还应当注意健康检查、容器指标以及最终的应用指标。 前期准备 在以下部分中,我们将以demo的形式逐一介绍列出的内置监控功能,为此我们需要做的前期准备有: 谷歌云平台帐户:使用免费试用版的即可,若你使用的是其他的主流云平台,操作方法也是类似的。 用于运行Rancher的主机:可以是个人PC / Mac或公有云中的VM。 谷歌云SDK:应与运行Rancher的主机上的kubectl一起安装。通过使用你的凭据进行身份验证(gcloud init和gcloud auth login),确保gcloud可以访问你的谷歌云帐户。 启动Rancher实例 第一步,启动Rancher实例。Rancher有一份非常直观的入门指南可供参考:https://rancher.com/quick-start/ 使用Rancher部署GKE集群 按照操作指南,使用Rancher设置和配置Kubernetes集群: https://rancher.com/docs/rancher/v2.x/en/cluster-provisioning/hosted-kubernetes-clusters/gke/ 注意:请确保已启用Kubernetes Dashboard,我们这里使用的Kubernetes 版本为v.1.10。 图1 使用Rancher创建Kubernetes集群 Kubernetes Dashboard Kubernetes dashboard是基于Web的Kubernetes用户界面,我们可以使用它来对应用程序进行故障排除并管理集群资源。 而Rancher,能帮助用户一键安装dashboard。dashboard的主要用途包括: 对集群资源进行概述(包括整体情况和每个node的情况),显示所有命名空间,列出定义的所有存储类 显示集群上运行的所有应用程序 提供关于集群中Kubernetes资源状态以及可能出现的任何错误的信息 要访问dashboard,我们需要在我们的计算机和Kubernetes API服务器之间代理请求。输入以下代码即可使用kubectl启动代理服务器: 代理服务器将在后台启动,输出类似于下文的内容: 现在,要查看dashboard,请通过浏览器访问以下地址: http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/ 然后,我们需要在登录页面输入相应的凭据: 图2 Dashboard登录 下面我们将来了解如何使用服务帐户机制,创建具有管理员权限的用户。我们将使用两个YAML文件。 一个YAML文件用于创建服务帐户: 另一个YAML文件将为我们的用户创建ClusterRoleBinding: 应用两个YAML文件,来创建其定义的对象: 创建用户并设置了正确的权限后,我们需要找到令牌才能登录: kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}') 在Kubernetes dashboard凭据提示中选择“Token(令牌)”,然后在字段中输入你在上面检索的值以进行身份验证。 Kubernetes Dashboard包含几个主要视图: 管理视图:列出了node、命名空间和持久卷以及其他详细信息。我们可以获得node的集成页面(CPU和内存使用情况)以及每个node的单独详细信息页面,显示其指标、规范、状态、分配的资源和pod。 工作负载视图:显示了在选定的命名空间中运行的所有应用程序。总结了关于工作负载的重要信息,例如StatefulSet或部署中准备好的pod数量或pod的当前内存使用情况。 服务发现和负载均衡视图:显示了向外部公开服务的Kubernetes资源,并在集群内启用服务发现。 配置和存储视图:显示了应用程序使用的持久卷声明资源。配置视图显示了用于在集群中运行的应用程序实时配置的所有Kubernetes资源。 在没有任何工作负载运行的情况下,dashboard页面将为空,因为此时在Kubernetes上不会部署任何内容。如果要浏览 dashboard提供的所有视图,最佳选择是部署使用不同工作负载类型的应用程序(StatefulSet、部署、副本集等)。这篇如何在Kubernetes上部署Redis的文章就是一个很好的示例,它展示了部署一个Redis集群(具有卷声明和configMaps的有状态集)和一个测试应用程序(一个Kubernetes部署)时,dashboard会如何显示相关信息。 配置完工作负载后,我们可以关闭一个node,然后检查不同的选项卡,以查看一些更新: 图3 Stateful Set的dashboard页面 图4 Pod的dashboard页面 cAdvisor cAdvisor是一个集成到kubelet二进制文件中的开源代理,主要用于监控资源使用情况并分析容器的性能。cAdvisor会收集关于在给定node上运行的所有容器的CPU、内存、文件和网络使用情况的统计信息(cAdvisor不在pod层运行)。除核心指标外,cAdvisor还会监控事件。用户可以使用诸如kubectl top之类的命令直接访问指标,也可以使用调度程序执行调度层的指标(例如使用autoscaling)。 需要注意的是,cAdvisor不会长期存储某些指标,因此如果需要使用该功能,则应寻找专用的监控工具。 从Kubernetes版本1.10起,cAdvisor的UI已经差不多被弃用了,Kubernetes 1.12版本之后cAdvisor的UI会被彻底删除。Rancher可以让你选择用于集群的Kubernetes版本。在为此演示设置基础架构时,我们将集群配置为使用版本1.10,因此我们仍然可以访问cAdvisor UI。 要访问cAdvisor UI,我们需要在我们的计算机和Kubernetes API服务器之间进行代理。输入以下命令启动代理服务器的本地实例: 接下来,找到node的名称: 你可以通过以下地址在浏览器中查看UI,将node名称替换为你在命令行中找到的标识符: http://localhost:8001/api/v1/nodes/gke-c-plnf4-default-pool-5eb56043-23p5:4194/proxy/containers/ 图5 初始cAdvisor UI 图6 cAdvisor UI概述和流程 为了确认kubelet正在监听端口4194,你可以登录到node查看更多信息: 我们可以确认,在我们的Kubernetes版本中,kubelet进程通过该端口提供cAdvisor Web UI: 如果你运行的Kubernetes版本为1.12或更高,因为cAdvisorUI已被删除,因此kubelet不会再监听4194端口了。你可以使用上面的命令进行确认。不过,由于cAdvisor是kubelet二进制文件的一部分,因此相关指标仍然存在。 kubelet二进制文件使用Prometheus展示格式公开了所有runtime和cAdvisor指标: http://localhost:8001/api/v1/nodes/gke-c-plnf4-default-pool-5eb56043-23p5/proxy/metrics/cadvisor 图7 cAdvisor指标端点 在一大堆输出中,你可以重点查找和关注的指标有: CPU: ocontainer_cpu_user_seconds_total:以秒为单位,“用户”累计消耗CPU的时间 ocontainer_cpu_system_seconds_total:以秒为单位,“系统”累计消耗CPU的时间 ocontainer_cpu_usage_seconds_total:以秒为单位,累计消耗CPU的时间(上述总和) 内存: ocontainer_memory_cache:页面缓存内存的字节数 ocontainer_memory_swap:容器交换使用情况,以字节为单位 ocontainer_memory_usage_bytes:当前内存使用情况,以字节为单位,包括所有内存 ocontainer_memory_max_usage_bytes:以字节为单位,最大内存使用量 磁盘: ocontainer_fs_io_time_seconds_total:执行I/O所花费的时间,以秒为单位 ocontainer_fs_io_time_weighted_seconds_total:累计加权I/O时间,以秒为单位 ocontainer_fs_writes_bytes_total:写入的累计字节数 ocontainer_fs_reads_bytes_total:读取的累计字节数 网络: ocontainer_network_receive_bytes_total:接收的累计字节数 ocontainer_network_receive_errors_total:接收时遇到的累计错误数 ocontainer_network_transmit_bytes_total:传输的累计字节数 ocontainer_network_transmit_errors_total:传输时遇到的累计错误数 一些其他有用的指标: / healthz:用于确定cAdvisor是否健康的端点 / healthz / ping:检查与etcd的连接状况 / spec:返回cAdvisor MachineInfo()的端点 例如,要查看cAdvisor MachineInfo(),我们可以访问: http://localhost:8001/api/v1/nodes/gke-c-plnf4-default-pool-5eb56043-23p5:10255/proxy/spec/ 图8 cAdvisor规范端点 pod端点为node上运行的pod提供与kubectl get pods -o json相同的输出: http://localhost:8001/api/v1/nodes/gke-c-plnf4-default-pool-5eb56043-23p5:10255/proxy/pods/ 图9 cAdvisor pod端点 同样,也可以通过访问以下链接来获取日志: http://localhost:8001/logs/kube-apiserver.log 结 语 监控的重要性不言而喻,它让我们能充分了解到应用程序的状况。Kubernetes有很多内置工具可供用户们选择,让大家更好地对基础架构层(node)和逻辑层(pod)有充分的了解。 在本文中,我们重点关注了专为用户提供监控和指标的工具。在本系列文章的下一篇中,我们将继续分享那些关注工作负载扩缩容和生命周期管理的监控工具,敬请期待。

优秀的个人博客,低调大师

原生js——四种对话框

js一共提供了四种对话弹框,即alert()、confirm()、prompt()、showModalDialog()。具体使用方法如下: 1.alert() alert()向用户显示一条消息并等待用户关闭对话框。 在大多数的浏览器里,alert()方法会产生阻塞,并等待用户关闭对话框。 也就是说,在用户关掉它们所显示的对话框之前,它们不会返回。这就意味着在弹出一个对话框前,代码就会停止运行。如果当前正在载入文档,也会停止载入,直到用户用要求的输入进行响应为止。 alert("我是一个alert对话框"); 2.confirm() confirm()也显示一条消息,要求用户单击“确定”或“取消”按钮,并返回一个布尔值。 在浏览器中,confirm()会产生阻塞。 var test_value = confirm("你确定要继续吗?"); console.log(test_value); // 如果点击【确定】则返回true,点击【取消】,返回false 3.prompt() prompt()同样也显示一条消息,等待用户输入字符串,并返回那个字符串。 在浏览器中,prompt()会产生阻塞。 var message = prompt("请输入你的名字:"); console.log(message); 4.showModalDialog() 首先要说明的是,之前的三种对话框是通用的,showModalDialog()有一定的兼容性,可以简单概括为:IE、火狐、safari浏览器是支持的。但是Chrome和opera浏览器是不支持的,不过我们可以采用window.open()来代替。 showModalDialog()显示一个包含HTML格式的“模态对话框”,可以给它传入参数,以及从对话框里返回值。 showModalDialog()在浏览器当前窗口中显示一个模态窗口,在显示模态窗口的时候,可以传入三个参数: 第一个参数用以指定提供对话框HTML内容的URL 第二个参数是一个任意值(数组和对象均可),这个值在对话框里的脚本中可以通过window.dialogArguments属性的值访问。 第三个参数是一个非标准的列表,包含以分号隔开的name=value对,如果提供了这个参数,可以配置对话框的尺寸或其他属性。具体属性如下: |属性|说明| |:---:|:---| |dialogHeight|对话框高度,不小于100px| |dialogWidth|对话框宽度。| |dialogLeft|离屏幕左的距离。| |dialogTop|离屏幕上的距离。| |center|{ yes | no | 1 | 0 }:是否居中,默认yes,但仍可以指定高度和宽度。| |help|{ yes | no | 1 | 0 }:是否显示帮助按钮,默认yes。| |resizable|{ yes | no | 1 | 0 } [IE5+]: 是否可被改变大小。默认no。| |status|{ yes | no | 1 | 0 } [IE5+]: 是否显示状态栏。默认为yes[ Modeless]或no[Modal]。| |scroll|{ yes | no | 1 | 0 | on | off }:是否显示滚动条。默认为yes。| 在进行模态弹窗的时候,我们首先应该新建一个弹窗的新页面,并在里面写出我们需要的html、css和js代码。例如我们新建一个modal_html.html页面: <form action="#"> <label for="user_name">Name:</label> <input type="text" id="user_name"> <br> <label for="user_gender">Gender:</label> <input type="text" id="user_gender"> <br> <label for="user_age">Age:</label> <input type="text" id="user_age"> </form> <button id="btn_ok">ok</button> <button id="btn_cancle">cancle</button> <script> // 获取页面中的三个input元素,并赋值 var user_name = document.getElementById('user_name'); var user_age = document.getElementById('user_age'); var user_gender = document.getElementById('user_gender'); user_name.value = window.dialogArguments[0]; user_age.value = window.dialogArguments[1]; user_gender.value = window.dialogArguments[2]; var btn_ok = document.getElementById('btn_ok'); var btn_cancle = document.getElementById('btn_cancle'); // 点击【ok】按钮,关闭并返回模态窗口中的值 btn_ok.addEventListener("click",function(){ window.returnValue = [] window.returnValue[0] = user_name.value; window.returnValue[1] = user_age.value; window.returnValue[2] = user_gender.value; window.close(); }); // 点击【cancel】,直接关闭模态窗口,不返回值 btn_cancle.addEventListener("click",function(){ window.close(); }); </script> 新的页面创建好之后,我们便可以在另外一个页面parent.html进行模态弹窗,按照要求传入三个参数,parent.html页面的代码如下: var p = window.showModalDialog("modal_html.html", ["Tom", 23, "meal"], "dialogWidth:400px;dialogHeight:200px;resizable:no"); // 上述代码等价于以下代码 var param_1 = "modal_html.html"; var param_2 = ["Tom", 23, "meal"]; var param_3 = "dialogWidth:400px;dialogHeight:200px;resizable:no"; var p = window.showModalDialog(param_1, param_2, param_3); 如果想从parent.html页面向模态窗口中传递数据,可以在模态窗口modal_html.html页面中,使用window.dialogArguments数组接受,示例代码如下: var user_name = document.getElementById('user_name'); var user_age = document.getElementById('user_age'); var user_gender = document.getElementById('user_gender'); user_name.value = window.dialogArguments[0]; user_age.value = window.dialogArguments[1]; user_gender.value = window.dialogArguments[2]; 如果想从模态窗口中向打开模态窗口的parent.html页面返回数据,首先要在parent.html页面设置window.returnValue,注意这里不需要使用return语句显式返回。使用window.close();语句关闭模态窗口的时候,会自动返回window.returnValue。事例代码如下: // 点击【ok】按钮,关闭并返回模态窗口中的值 btn_ok.addEventListener("click",function(){ window.returnValue = [] window.returnValue[0] = user_name.value; window.returnValue[1] = user_age.value; window.returnValue[2] = user_gender.value; window.close(); }); 在模态窗口页面设置好window.returnValue,我们便可以在parent.html页面进行引用,示例代码如下: var p = window.showModalDialog("modal_html.html", ["Tom", 23, "meal"], "dialogWidth:400px;dialogHeight:200px;resizable:no"); alert(p[0]); // 如果在模态窗口中不修改,则为Tom alert(p[1]); // 如果在模态窗口中不修改,则为 23 alert(p[2]); // 如果在模态窗口中不修改,则为 meal

优秀的个人博客,低调大师

原生jdbc操作mysql数据库详解

首先给大家说一下使用JDBC链接数据库的步骤 1.加载链接数据库驱动 2.建立数据库链接 3.创建数据库操作对象 4.编写sql语句,执行sql语句 5.获取结果集 6.释放资源 我这边采用的是maven构建工具创建的java项目;如果不使用maven创建的话也没有什么不同,因为都是在main方法里面写的嘛! 只是需要导入jdbc需要的jar包即可,然后下面建项目的这些步骤就可以直接跳过了 首先点击新建项目 如果你的这里没有Maven Project 这个选项的话,那就直接点击Other,有的话就直接点击Maven项目即可! 点完以后在这里搜索maven也是一样的,选择Maven Project 然后next 直接使用默认 继续next 找到我选中的那一条 ,其他地方不用填 ,继续next 主要目的不是为了构建项目,所以这上面两个需要填写的地方可以随意些,上面的代表包名 ,第二个代表项目名,然后Finish 然后在pom.xml里面加入如下配置;我是不记得需要那几个jar包了,所以直接全部放进去了,哈哈哈哈哈 现在开始就要写代码了,首先建一个main方法,很简单的写一个例子 package com.TestJDBC; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * Hello world! * */ public class Demo01 { public static void main( String[] args ) { Connection connection=null ; Statement statement=null; ResultSet resultSet = null; try { //1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); //2.获取连接对象(参数解释,第一个是代表jdbc的方式访问mysql数据库,localhost代表是访问本地的,3306端口号,db_test01代表数据库名字,后面两个代表账户密码 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_test01","root","root"); //3.获取执行sql对象 statement = connection.createStatement(); //4.编写sql语句,执行sql String sql="select * from stu"; //5.获取结果集 ,查询操作使用executeQuery,增,删,改使用statement.executeLargeUpdate(sql) //如果是增删改操作,失败了会返回0 resultSet = statement.executeQuery(sql); if(resultSet.next()){ //获取字符串类型的值 String name = resultSet.getString("name"); String sex = resultSet.getString("sex"); //获取int型的值 int age = resultSet.getInt("age"); System.out.println(name+" ,"+sex+","+age); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { //6.释放连接 if(resultSet!=null){ try { resultSet.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(statement!=null){ try { statement.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(connection!=null){ try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } 下面改了一下,是一个预编译的案例 package com.TestJDBC; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * Hello world! * */ public class Demo01 { public static void main(String[] args) { Connection connection = null; PreparedStatement statement = null; ResultSet resultSet = null; try { // 1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); // 2.获取连接对象(参数解释,第一个是代表jdbc的方式访问mysql数据库,localhost代表是访问本地的,3306端口号,db_test01代表数据库名字,后面两个代表账户密码 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_test01", "root", "root"); // 3.获取执行sql对象 // 4.编写sql语句,执行sql(预编译,用?号代替值) String sql = "select * from stu where id=?"; statement = connection.prepareStatement(sql); //然后再设置进去,可以设置多个值 statement.setString(1, "1"); resultSet = statement.executeQuery(); // 5.获取结果集 // ,查询操作使用executeQuery,增,删,改使用statement.executeLargeUpdate(sql) // 如果是增删改操作,失败了会返回0 if (resultSet.next()) { // 获取字符串类型的值 String name = resultSet.getString("name"); String sex = resultSet.getString("sex"); // 获取int型的值 int age = resultSet.getInt("age"); System.out.println(name + " ," + sex + "," + age); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { // 6.释放连接 if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (statement != null) { try { statement.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }

优秀的个人博客,低调大师

DevOps助力云原生应用开发实战分享

背景 Cloud Foundry 是业界比较“资深”的PAAS云平台,它不仅支持多种框架、运行时环境,还支持在多种云环境进行部署,包括:AWS, Azure, GCP, OpenStack等。本文分享,基于阿里云进行开发、运维的实战经验。 部署Cloud Foundry首先要用到的工具是BOSH。BOSH是用来部署和管理Cloud Foundry集群的工具。 它定义了一系列管理和操作云资源的接口Cloud Provider Interface,各个云厂商需要适配自己的Provider,这就是本文的开发背景。为了让大家对BOSH和CPI有一个感性的认识,下面简要的介绍几个相关概念。 BOSH的组件结构如上图所示, 这里简单说明一下,详细说明见官方文档 Director会解析部署命令和模板,然后调用CPI模块去创建VM(ECS)实例,实例信

优秀的个人博客,低调大师

Android 原生 Intent 分享支持的那些事

一、前言 对于一个 App 而言,分享是一个比较常见的功能。分享的主要功能,还是为了让 App 在用户之间形成一个自传播的效果,但是对于开发者而言,分享就是在不同的 App 之间,传递文本、文件等数据。 如果只是简单的分享内容,使用ACTION_SEND或者ACTION_SEND_MULTIPLE可能是一种简单的方式。可是从 Support v4 22.0.0 开始,引入了一个新的分享兼容库,ShareCompat,它可以帮开发者更轻松的构造一个分享的 Intent 。 二、ShareCompat 2.1 什么是 ShareCompat ShareCompat 可以帮助我们快速的实现应用间分享的数据的功能。既然是一个完整的功能,它主要包含了两个功能:快速的构建一个分享的 Intent,快速的读取别的 App 内分享来的内容。 所以,ShareCompat 是包含两部分内容的:IntentBuilder、IntentReader。从名称上就可以看出来他们的用途。 2.2 分享文本 从最简单的开始,使用 IntentBuilder 分享一个纯文本,是非常简单的,IntentBuilder 从名称也能看出来,它是Builder模式,所以可以支持多个方法链式调用。 其中最重要的是,设置一个正确的mimeType,它将是用于Intent-filter过滤器重要的过滤条件,表示这里知识分享一个纯文本的类型的内容。 在startActivity()之前,调用resolveActivity()做一层校验,是一个比较常规的保护措施。它避免了当前系统中没有任何 App 可以匹配上这个隐式的 Intent,而触发ActivityNotFoundException的异常。虽然没有可以接收text/plain类型的分享其实是非常不可能的,但是加上它也是一个很好的编码习惯。 2.3 分享 HTML 格式的文本 有一些 App ,例如 电子邮件客户端,是可以支持 HTML 格式的文本的,与纯文本相比,它可以显示更丰富的内容。 可以注意到,区别就在于setType()的时候,填写的是text/html,并且使用的是setHtmlText()方法去配置分享的内容。 既然分享 Html 内容,多用于发送 Email 内容,所以这里也配置了接收的邮件地址。 一般而言,使用 Html 的方式还需要接收分享内容的 App 支持这样的内容,所以还是需要谨慎使用,可能用户分享的 App ,并不能很好的显示我们需要分享的 Html 的内容。 2.4 发送一个Email 前面也提到如何在发送的 Email 中,添加一个 Html 的内容。而 ShareCompat 也提供了非常好的发送 Email 的体验,可以非常方便的构造一个用于发送 Email 的 Intent。 IntentBuilder 中,提供了非常方便的 Api,用于添加一些 Email 收件人、抄送之类的数据,都在addEmailXxx()的方法中,非常好理解,有兴趣的可以直接看看 Api 文档。 2.5 收一个文本内容 虽然大多数情况下,我们是需要开发一个支持分享的功能,但是也不影响我们了解如何接收一个分享的内容。 而分享传递过来的内容,都是可以在 Intent 中获取到的。而 IntentReader 也提供了非常便捷的 Api 让我们快速的拿到它的内容。 这里以接收一个文本内容为例,首先需要在AndroidManifest.xml中配置接收的 Activity 和 Intent-filter。 这就是一个比较常规的 ShareActivity 的配置。 IntentReader 使用起来也非常的简单,它实际上就是一个 Intent 数据的包装器,提供了很多方便的 Api ,从 Intent 中提取出我们需要的数据。 2.6 分享文件和图片 除了分享一段文本内容之外,有时候我们还需要向外分享一个文件(包括视频、图片等)。而发送这些,就需要具有额外的权限。 构造一个图片的分享也非常的简单,只需要使用setStream()方法,传递一个文件的Uri即可,并且配置好对应的mimeType。 但是这种方式,就需要考虑到 Android 6.0 开始对分享的文件,以严格模式进行处理,所以对此我们需要使用FileProvider来支持。 有关 FileProvider 的内容,可以看看之前的文章: 一旦使用上 FileProvider 只需要将 Uri 替换成FileProvider.getUriForFile()返回的 Uri 即可,这都是 FileProvider 的标准内容,就不在这里详细说明了。 不过有一点需要特别注意的,使用 FileProvider 返回的 Uri ,就可以不用显式的设定 mimeType 类型了,因为它是可以通过这个 Uri 推断出正确的 mimeType 的类型的,所以就不需要显式的指定了。 2.7 接收文件内容 Android 6.0 对文件的接收,并没有任何的改变,所以我们只需要使用IntentReader.getStream()这个标准的 Api 就可以拿到分享过来的 Uri。 三、ShareCompat 都兼容了什么 Support v4 包下的库,其实都是为了向 Android 低版本做兼容而努力的,让开发者无需关注他们之间的差异,而通常我们看到一个类的命名中,有 Compat 字样,一般就可以断定,它又是一个兼容库。 那么 ShareCompat 对不同的 Android 版本,有做什么兼容处理呢? ShareCompat 中,有一个静态的 ShareCompatImpl 对象,它就是为了出来兼容问题的,它是一个接口,先来看看它的定义。 可以看到,它实际上就是对configureMenuItem()和 html 的文本转义,做了兼容的处理。 在 ShareCompat 的静态代码块中,对其进行版本的区分,不同版本用不同的实现类。 简单来说,就是一些处理方法,可能在低版本上没有,所以它会使用另外的方法去实现。举个例子,ShareCompatImplJB中,escapeHtml()方法,最终调用的是Html.escapeHtml()方法,但是这个方法它是 Api Level 16 才有的支持,所以在ShareCompatImplBase中,实际上是把它的实现,完全的拷贝了出来,只是为了做一个 Html 中字符的转义。 四、结语 ShareCompat 如果只是简单的去分享一个内容,还是非常的好用的。但是大多数情况下,我们还是会选择使用第三方 App 提供的 SDK,来进行接入支持,因为会有一些定制的需要,例如拿到分享结果。 不过这并不影响我们了解 ShareCompat ,多看看 Google 开发人员写的代码,终归是对你有帮助的。 本文转自承香墨影博客园博客,原文链接:http://www.cnblogs.com/plokmju/p/7324776.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

原生的 exFAT 支持已经来到 Linux 平台

不同文件系统在不同的操作系统上读写一直是件很头大的事,特别是用 NTFS/exFAT格式化后的大容量U盘,在 Windows 和 Linux 之间共享数据时最为头痛。 在Linux平台上支持exFAT,分为三个阶段: 最初是一个exFAT的补丁,可以只读的使用exFAT;之后通过FUSE方式支持了exFAT,但是其性能并不很好;现在由Android代码中移植来的exfat-nofuse则是由微软开发的,并不依赖FUSE,不但能支持读写,而且性能也不错。 exfat-nofuse在 Linux kernel 3.8 、 3.9和3.10中通过测试,可需求的同学可以前往github下载: https://github.com/rxrz/exfat-nofuse 安装方式: make && make install modprobe exfat_fs 原文发布时间为:2013-07-06 本文来自云栖社区合作伙伴“Linux中国”

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册