首页 文章 精选 留言 我的

精选列表

搜索[k8s],共3940篇文章
优秀的个人博客,低调大师

红帽发布 OpenShift 开发者沙盒,加速 K8s 应用开发

红帽近日发布了针对红帽 OpenShift 的开发者沙盒,这是一个基于 OpenShift 的开发环境,旨在让企业加快基于 Kubernetes 的应用从代码到生产的过程。OpenShift 沙盒为开发者提供了一种更简单、无成本的方式,让开发者可以使用将在生产环境中运行的相同基础结构和工具来开始构建应用程序,而不必担心配置问题。 通过 OpenShift 沙盒和我们最新更新的开发者工具,使开发者更容易为 Kubernetes 进行构建。 红帽开发者工具和项目部副总裁 Mithun Dharv 表示,如今开发人员面临着更快地交付应用程序的压力,同时要跟上快速变化的技术环境。数字化转型工作的加速推动了这一点,以将新的创新推向市场,而围绕 IT 现代化和应用开发的必要性也在不断增强。Kubernetes 是混合云架构的一个基础平台。通过消除开发人员为 Kubernetes 构建应用程序的障碍,红帽正在使开发人员能够加速开发过程。 OpenShift 开发者沙盒在一个共享的多租户集群中提供了一个私有的 OpenShift 环境,并预先配置了一套开发者工具。该基础设施和工具是紧密集成的,旨在为原型设计或构建新的应用程序、添加新的服务、从源代码或 Docker 文件创建容器等提供一个安全的环境。 结合红帽的开发者工具,这些新功能帮助开发者更好地应对提高应用速度的需求,并进一步巩固红帽 OpenShift 作为构建、部署和管理企业 Kubernetes 应用的领先平台。

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

K8S 太强了,我搞了这么多破坏居然还没宕机

作者 | Andrei Kvapil 编译|云原生计算编辑部 原文链接:https://itnext.io/breaking-down-and-fixing-kubernetes-4df2f22f87c3 Kubernetes 是当下最流行的容器编排平台,不管是生产环境的采用率,还是云原生生态都很强大。最近,Kubernetes 在功能,安全性和弹性方面取得了长足的进步,Kubernetes 架构使您可以轻松地应对各种故障并始终保持正常运行。 不相信它这么顽强?下面,我们将破坏集群,删除证书......一起来看看这些破坏性的动作会不会让运行的服务宕机。 在 Kubernetes 控制平面(主)节点上仅包含以下几个组件: etcd —用作数据库 kube-apiserver — 提供集群所的API服务,负责在Kubernetes节点和Kubernetes主组件之间建立通信。 kube-controller-manager —对Kubernetes资源执行操作 kube-scheduler —主调度程序 kubelet —负责容器的创建与起停 以上每个组件都受到一组针对客户端和服务器的TLS证书的保护。 它们用于对彼此之间的组件进行身份验证和授权。 它们一般不会存储在 Kubernetes 数据库中,而是以普通文件的形式存在: # tree /etc/kubernetes/pki/ /etc/kubernetes/pki/ ├── apiserver.crt ├── apiserver-etcd-client.crt ├── apiserver-etcd-client.key ├── apiserver.key ├── apiserver-kubelet-client.crt ├── apiserver-kubelet-client.key ├── ca.crt ├── ca.key ├── CTNCA.pem ├── etcd │ ├── ca.crt │ ├── ca.key │ ├── healthcheck-client.crt │ ├── healthcheck-client.key │ ├── peer.crt │ ├── peer.key │ ├── server.crt │ └── server.key ├── front-proxy-ca.crt ├── front-proxy-ca.key ├── front-proxy-client.crt ├── front-proxy-client.key ├── sa.key └── sa.pub 这些组件作为静态 pod 在 master 节点上运行,资源清单在/ etc / kubernetes / manifests目录下。 关于这一点,我们将不做详细介绍, 在本案例中,我们主要对如何从所有这些组件中获得一个有效运行的集群感兴趣。 首先,假设我们拥有上述 Kubernetes 组件,它们以某种方式相互通信。 通常如下所示: (箭头表示客户端到服务器的连接) 为了进行通信,他们需要TLS证书,部署工具可以是 kubeadm,kubespray 或其他任何工具。 在本文中,我们将使用kubeadm,因为它是最常见的 Kubernetes 部署工具,并且经常在其他解决方案中使用。 假设我们已经有一个部署好的集群。 让我们开始破坏行动吧: rm -rf /etc/kubernetes/ 在 master节点上,此目录包含: -etcd的一组证书和CA(在/ etc / kubernetes / pki / etcd中) -Kubernetes 的一组证书和CA(在/ etc / kubernetes / pki中) -用于cluster-admin,kube-controller-manager,kube-scheduler和kubelet使用的Kubeconfig文件(每个文件在/etc/kubernetes/*.conf中都有针对我们集群的base64编码的CA证书) -etcd,kube-apiserver,kube-scheduler和kube-controller-manager使用的static manifest文件(在/ etc / kubernetes / manifests中) 假设我们一下子把什么都删了...... 修复控制平面 为避免混淆,我们还要确保所有control-plane pods也已停止: crictl rm $(crictl ps -aq) 注意:默认情况下,kubeadm 不会覆盖现有证书和 kubeconfig,要重新发布它们,必须首先手动删除旧证书和 kubeconfig。 接下来,让我们从恢复 etcd开始。 如果您有一个 quorum(3个或更多主节点),则直到大多数节点都联机后,才能访问 etcd群集。 kubeadm init phase certs etcd-ca 上面的命令将为我们的 etcd 集群生成一个新的CA。 由于所有其他证书都必须由它签名,因此,我们还将其和私钥复制到其他 master节点: /etc/kubernetes/pki/etcd/ca.{key,crt} 现在,让我们在所有控制平面节点上为其重新生成其余的 etcd 证书和static-manifest: kubeadm init phase certs etcd-healthcheck-client kubeadm init phase certs etcd-peer kubeadm init phase certs etcd-server kubeadm init phase etcd local 此时,您应该已经了有一个可以正常工作的 etcd 集群: # crictl ps CONTAINER ID IMAGE CREATED STATE NAME ATTEMPT POD ID ac82b4ed5d83a 0369cf4303ffd 2 seconds ago Running etcd 0 bc8b4d568751b 然后,我们执行相同的任务,但是对于 Kubernetes 服务,在 master节点之一上执行: kubeadm init phase certs all kubeadm init phase kubeconfig all kubeadm init phase control-plane all cp -f /etc/kubernetes/admin.conf ~/.kube/config 如果您使用 kubeadm 来连接 kubeletes,则还需要更新kube-public namespace中的 cluster-info 配置,因为它仍然包含旧CA的哈希。 kubeadm init phase bootstrap-token 由于其他实例上的所有证书也必须由单个CA签名,因此我们将其复制到其他控制平面节点,然后在每个证书上重复上述命令。 /etc/kubernetes/pki/{ca,front-proxy-ca}.{key,crt} /etc/kubernetes/pki/sa.{key,pub} 顺便说一下,作为手动复制证书的替代方法,您还可以使用 Kubernetes API,例如以下命令: kubeadm init phase upload-certs --upload-certs 加密并上传证书到Kubernetes 需要2小时,因此您可以按以下方式注册 master 节点: kubeadm join phase control-plane-prepare all kubernetes-apiserver:6443 --control-plane --token cs0etm.ua7fbmwuf1jz946l --discovery-token-ca-cert-hash sha256:555f6ececd4721fed0269d27a5c7f1c6d7ef4614157a18e56ed9a1fd031a3ab8 --certificate-key 385655ee0ab98d2441ba8038b4e8d03184df1806733eac131511891d1096be73 kubeadm join phase control-plane-join all 请注意,Kubernetes API具有另一个配置,用于保存 front-proxy客户端的CA证书。 它用于验证从 apiserver 到 webhooks 和聚合层服务的请求。 幸运的是,kube-apiserver 会自动更新它。 但是,您可能需要手动从旧证书中清除它: kubectl get cm -n kube-system extension-apiserver-authentication -o yaml 无论如何,在这个阶段,我们已经有一个可以正常工作的控制平面。 修复工作节点 该命令将列出集群的所有节点,尽管当前所有节点的状态均为“NotReady”: kubectl get node 这是因为它们仍然使用旧证书,并期望来自由旧CA签名的 apiserver 的请求。 为了解决这个问题,我们将使用 kubeadm,并对群集执行重新加入节点。 当主节点可以访问主CA时,它们可以在本地加入: systemctl stop kubelet rm -rf /var/lib/kubelet/pki/ /etc/kubernetes/kubelet.conf kubeadm init phase kubeconfig kubelet kubeadm init phase kubelet-start 但是要加入工作节点,我们必须生成一个新token: kubeadm token create --print-join-command 并分别对它们运行以下命令: systemctl stop kubelet rm -rf /var/lib/kubelet/pki/ /etc/kubernetes/pki/ /etc/kubernetes/kubelet.conf kubeadm join phase kubelet-start kubernetes-apiserver:6443 --token cs0etm.ua7fbmwuf1jz946l --discovery-token-ca-cert-hash sha256:555f6ececd4721fed0269d27a5c7f1c6d7ef4614157a18e56ed9a1fd031a3ab8 注意:您不需要删除主节点上的/ etc / kubernetes / pki /目录,因为它已经包含所有需要的证书。 上面的操作会将您的所有 kubelet 重新加入集群。 它不会影响已经在其上运行的任何容器。 但是,如果集群中有多个节点并且不同时进行,则可能会遇到一种情况,即controller-manager开始从NotReady节点重新创建容器,并尝试在alive的节点上重新调度它们。 为了防止这种情况,我们可以暂时停止 master上的controller-manager pod: rm /etc/kubernetes/manifests/kube-controller-manager.yaml crictl rmp $(crictl ps --name kube-controller-manager -q) 仅需要最后一条命令即可确保controller-manager已真正停止。 一旦集群中的所有节点被加入,就可以为controller-manager生成一个static-manifest。 为此,请在所有 master节点上运行以下命令: kubeadm init phase control-plane controller-manager 请注意,您需要在已经生成join token的阶段执行这些步骤。 否则,join过程将继续尝试从cluster-info configmap读取token。 如果将kubelet配置为请求由您的CA签名的证书(选项serverTLSBootstrap:true),则还需要批准来自kubelet的CSR请求。 kubectl get csr kubectl certificate approve <csr> 修复ServiceAccounts 还有一件事,因为我们丢失了/etc/kubernetes/pki/sa.key。 该密钥用于为群集中所有 ServiceAccounts 的jwt令牌签名。 因此,我们必须为其重新创建token。 通过从kubernetes.io/service-account-token类型的Secret中删除token字段,可以非常简单地完成此操作: kubectl get secret --all-namespaces | awk '/kubernetes.io\/service-account-token/ { print "kubectl patch secret -n " $1 " " $2 " -p {\\\"data\\\":{\\\"token\\\":null}}"}' | sh -x 之后,kube-controller-manager将自动生成使用新密钥签名的新token。 不幸的是,并非所有的微服务都能即时更新token,因此很可能需要手动重新启动所有使用token的容器。 kubectl get pod --field-selector 'spec.serviceAccountName!=default' --no-headers --all-namespaces | awk '{print "kubectl delete pod -n " $1 " " $2 " --wait=false --grace-period=0"}' 例如,此命令将生成命令列表,以使用非默认 serviceAccount删除所有pod。 我建议从kube-system的namespace开始,因为在那里安装了kube-proxy和CNI插件。 它们对于处理微服务之间的通信至关重要。 此时,集群恢复就算完成了。 Kubernetes 在生产环境如何落地? 目前,Kubernetes 技术已然成为了容器编排领域的事实标准。百度从 2010 年开始探索容器和集群管理技术,2016年自主研发的 Matrix 集群管理系统已经管理了数十万台机器和服务。随着 Kubernetes 技术的成熟,百度看到了开源技术强大的生命力,从 2018 年开始尝试向 Kubernetes 架构演化。试点成功之后,启动了大规模 Kubernetes 架构融合项目。一方面保留百度在 Matrix 上积累的核心技术能力,另一方面让存量业务可以更低成本的迁移到 Kubernetes 之上。 百度于 2020 年获得 InfoQ 十大云原生创新技术方案,对百度云原生来说仅仅是个开始。目前大规模 Kubernetes 融合架构的业务正在百度云原生各产品技术架构中稳定运行并持续增长,百度云原生团队也将会在继续服务好客户的同时,利用Kubernetes技术实践经验不断优化产品,更好地助力各行各业的客户实现基于云原生架构的数字化转型。 百度智能云云原生平台,为客户建设容器化和无服务器化的基础设施,提供企业级的微服务治理能力,同时集成源自百度自身多年实践的DevOps工具链。保障开发者享受到高效、灵活、弹性的开发与运维体验,助力企业更高效率低风险地构建云原生应用,广泛应用于金融、互联网、制造等各行各业的云原生转型阶段。 了解更多百度云原生产品请访问: https://cloud.baidu.com/product/cnap.html 重磅!云原生计算交流群成立 扫码添加小助手即可申请加入,一定要备注:名字-公司/学校-地区,根据格式备注,才能通过且邀请进群。。 了解更多微服务、云原生技术的相关信息,请关注我们的微信公众号【云原生计算】!

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

记一次k8s修改docker存储路径引发的问题

背景: 测试环境安装centos7的时候没有手动分区,自动分区根目录只有50G,而安装docker的时候也没有指定存储路径,默认是/var/lib/docker,我在/home底下创建一个目录docker_data/docker,然后将/var/lib/docker目录内容拷贝到/home/docker_data/docker目录,然后做软链接将/var/lib/docker指定到/home/docker_data/docker,分别将三个节点都这样做,刚开始做完都没有问题,后面有一次重启docker的时候有一个节点上面的pod就启动不起来了,使用kubectl describe pod 后得到以下信息 failed to inspect sandbox image "registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0": Error response from daemon: layer does not exist 分析:1、刚开始以为是这个镜像不存在了,使用docker pull重新拉取镜像,也提示拉取成功了 [root@node02 ~]# docker pull registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0 3.0: Pulling from google-containers/pause-amd64 Digest: sha256:3b3a29e3c90ae7762bdf587d19302e62485b6bef46e114b741f7d75dba023bd3 Status: Image is up to date for registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0 registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0 但是使用docker images查看没有任何镜像 [root@node02 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE 2、我以为是缓存的问题,使用docker rmi 想删除这个镜像,提示没有这个镜像 [root@node02 ~]# docker rmi registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0 Error response from daemon: unrecognized image ID sha256:99e59f495ffaa222bfeb67580213e8c28c1e885f1d245ab2bbe3b1b1ec3bd0b2 3、问别人说可能是缓存镜像层的问题,要清除缓存镜像层,于是我使用以下命令 [root@node02 ~]# docker system prune -a WARNING! This will remove: - all stopped containers - all networks not used by at least one container - all images without at least one container associated to them - all build cache Are you sure you want to continue? [y/N] y Deleted Images: untagged: bitnami/node-exporter:1.0.1-debian-10-r40 untagged: bitnami/node-exporter@sha256:93b7b6e1decabb3785af9d8a4638457264086eff0e39c88b33cb28a1bf5eb56c deleted: sha256:bf3932c1a438bd5a370678277c55d6c0d96fc9b319da1a19bf7022542da97459 deleted: sha256:5457ebcdc2664ba370621a2747cbecbdda9f816f9bb372582445c6f07e942966 deleted: sha256:3b21c23574ea3a44256c9fd9ab20ccaa876fd19187ed8804a18d3f76cfc0380e deleted: sha256:af30bbe16627da3074b8932259e2021f3560e1bedf69b5dd014a3b5d10f2c6bc deleted: sha256:6f3a3e041f64bd68c124667d904bde76d95a57d596363ee4884ff938e6d4f471 deleted: sha256:5dd3dec5dec64a9944ed3e8d0fba5f209d5a91da387efb77aa96e1c68581de4a deleted: sha256:049f84c2a771aef6f164f4e055aabdfcad2d173b5473f1277a47ff29e03e5a8d untagged: busybox:1.28.3 untagged: busybox@sha256:58ac43b2cc92c687a32c8be6278e50a063579655fe3090125dcb2af0ff9e1a64 deleted: sha256:8ac48589692a53a9b8c2d1ceaa6b402665aa7fe667ba51ccc03002300856d8c7 deleted: sha256:0314be9edf00a925d59f9b88c9d8ccb34447ab677078874d8c14e7a6816e21e1 Total reclaimed space: 116.9MB 这个命令清除所有没有使用的镜像和容器,清除后仍然拉到不到镜像 4、大佬说可能还是缓存没有清理干净,于是我就删除以下目录 [root@node02 ~]# ls /home/docker_data/docker/image/overlay2/layerdb/ 删除了,重启docker,再拉取,再docker images,仍然没有看到镜像 5、我干脆直接删除image目录 [root@node02 ~]# rm /home/docker_data/docker/image 再重启docker,再拉取镜像,再docker images,这回有镜像了,我再Kubectl delete pod删除相关pod,后面就正常重建Pod了

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

详细教程丨使用Prometheus和Thanos进行高可用K8S监控

本文转自Rancher Labs 介 绍 Prometheus高可用的必要性 在过去的几年里,Kubernetes的采用量增长了数倍。很明显,Kubernetes是容器编排的不二选择。与此同时,Prometheus也被认为是监控容器化和非容器化工作负载的绝佳选择。监控是任何基础设施的一个重要关注点,我们应该确保我们的监控设置具有高可用性和高可扩展性,以满足不断增长的基础设施的需求,特别是在采用Kubernetes的情况下。 因此,今天我们将部署一个集群化的Prometheus设置,它不仅能够弹性应对节点故障,还能保证合适的数据存档,供以后参考。我们的设置还具有很强的可扩展性,以至于我们可以在同一个监控保护伞下跨越多个Kubernetes集群。 当前方案 大部分的Prometheus部署都是使用持久卷的pod,而Prometheus则是使用联邦机制进行扩展。但是并不是所有的数据都可以使用联邦机制进行聚合,在这里,当你增加额外的服务器时,你往往需要一个机制来管理Prometheus配置。 解决方法 Thanos旨在解决上述问题。在Thanos的帮助下,我们不仅可以对Prometheus的实例进行多重复制,并在它们之间进行数据去重,还可以将数据归档到GCS或S3等长期存储中。 实施过程 Thanos 架构 图片来源: https://thanos.io/quick-tutorial.md/ Thanos由以下组件构成: Thanos sidecar:这是运行在Prometheus上的主要组件。它读取和归档对象存储上的数据。此外,它还管理着Prometheus的配置和生命周期。为了区分每个Prometheus实例,sidecar组件将外部标签注入到Prometheus配置中。该组件能够在 Prometheus 服务器的 PromQL 接口上运行查询。Sidecar组件还能监听Thanos gRPC协议,并在gRPC和REST之间翻译查询。 Thanos 存储:该组件在对象storage bucket中的历史数据之上实现了Store API,它主要作为API网关,因此不需要大量的本地磁盘空间。它在启动时加入一个Thanos集群,并公布它可以访问的数据。它在本地磁盘上保存了少量关于所有远程区块的信息,并使其与 bucket 保持同步。通常情况下,在重新启动时可以安全地删除此数据,但会增加启动时间。 Thanos查询:查询组件在HTTP上监听并将查询翻译成Thanos gRPC格式。它从不同的源头汇总查询结果,并能从Sidecar和Store读取数据。在HA设置中,它甚至会对查询结果进行重复数据删除。 HA组的运行时重复数据删除 Prometheus是有状态的,不允许复制其数据库。这意味着通过运行多个Prometheus副本来提高高可用性并不易于使用。简单的负载均衡是行不通的,比如在发生某些崩溃之后,一个副本可能会启动,但是查询这样的副本会导致它在关闭期间出现一个小的缺口(gap)。你有第二个副本可能正在启动,但它可能在另一个时刻(如滚动重启)关闭,因此在这些副本上面的负载均衡将无法正常工作。 Thanos Querier则从两个副本中提取数据,并对这些信号进行重复数据删除,从而为Querier使用者填补了缺口(gap)。 Thanos Compact组件将Prometheus 2.0存储引擎的压实程序应用于对象存储中的块数据存储。它通常不是语义上的并发安全,必须针对bucket 进行单例部署。它还负责数据的下采样——40小时后执行5m下采样,10天后执行1h下采样。 Thanos Ruler基本上和Prometheus的规则具有相同作用,唯一区别是它可以与Thanos组件进行通信。 配 置 前期准备 要完全理解这个教程,需要准备以下东西: 对Kubernetes和使用kubectl有一定的了解。 运行中的Kubernetes集群至少有3个节点(在本demo中,使用GKE集群) 实现Ingress Controller和Ingress对象(在本demo中使用Nginx Ingress Controller)。虽然这不是强制性的,但为了减少创建外部端点的数量,强烈建议使用。 创建用于Thanos组件访问对象存储的凭证(在本例中为GCS bucket)。 创建2个GCS bucket,并将其命名为Prometheus-long-term和thanos-ruler。 创建一个服务账户,角色为Storage Object Admin。 下载密钥文件作为json证书,并命名为thanos-gcs-credentials.json。 使用凭证创建Kubernetes sercret kubectl create secret generic thanos-gcs-credentials --from-file=thanos-gcs-credentials.json 部署各类组件 部署Prometheus服务账户、Clusterroler和Clusterrolebinding apiVersion: v1 kind: Namespace metadata: name: monitoring --- apiVersion: v1 kind: ServiceAccount metadata: name: monitoring namespace: monitoring --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: name: monitoring namespace: monitoring rules: - apiGroups: [""] resources: - nodes - nodes/proxy - services - endpoints - pods verbs: ["get", "list", "watch"] - apiGroups: [""] resources: - configmaps verbs: ["get"] - nonResourceURLs: ["/metrics"] verbs: ["get"] --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: monitoring subjects: - kind: ServiceAccount name: monitoring namespace: monitoring roleRef: kind: ClusterRole Name: monitoring apiGroup: rbac.authorization.k8s.io --- 以上manifest创建了Prometheus所需的监控命名空间以及服务账户、clusterrole以及clusterrolebinding。 部署Prometheues配置configmap apiVersion: v1 kind: ConfigMap metadata: name: prometheus-server-conf labels: name: prometheus-server-conf namespace: monitoring data: prometheus.yaml.tmpl: |- global: scrape_interval: 5s evaluation_interval: 5s external_labels: cluster: prometheus-ha # Each Prometheus has to have unique labels. replica: $(POD_NAME) rule_files: - /etc/prometheus/rules/*rules.yaml alerting: # We want our alerts to be deduplicated # from different replicas. alert_relabel_configs: - regex: replica action: labeldrop alertmanagers: - scheme: http path_prefix: / static_configs: - targets: ['alertmanager:9093'] scrape_configs: - job_name: kubernetes-nodes-cadvisor scrape_interval: 10s scrape_timeout: 10s scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token kubernetes_sd_configs: - role: node relabel_configs: - action: labelmap regex: __meta_kubernetes_node_label_(.+) # Only for Kubernetes ^1.7.3. # See: https://github.com/prometheus/prometheus/issues/2916 - target_label: __address__ replacement: kubernetes.default.svc:443 - source_labels: [__meta_kubernetes_node_name] regex: (.+) target_label: __metrics_path__ replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor metric_relabel_configs: - action: replace source_labels: [id] regex: '^/machine\.slice/machine-rkt\\x2d([^\\]+)\\.+/([^/]+)\.service$' target_label: rkt_container_name replacement: '${2}-${1}' - action: replace source_labels: [id] regex: '^/system\.slice/(.+)\.service$' target_label: systemd_service_name replacement: '${1}' - job_name: 'kubernetes-pods' kubernetes_sd_configs: - role: pod relabel_configs: - action: labelmap regex: __meta_kubernetes_pod_label_(.+) - source_labels: [__meta_kubernetes_namespace] action: replace target_label: kubernetes_namespace - source_labels: [__meta_kubernetes_pod_name] action: replace target_label: kubernetes_pod_name - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] action: keep regex: true - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scheme] action: replace target_label: __scheme__ regex: (https?) - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] action: replace target_label: __metrics_path__ regex: (.+) - source_labels: [__address__, __meta_kubernetes_pod_prometheus_io_port] action: replace target_label: __address__ regex: ([^:]+)(?::\d+)?;(\d+) replacement: $1:$2 - job_name: 'kubernetes-apiservers' kubernetes_sd_configs: - role: endpoints scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token relabel_configs: - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] action: keep regex: default;kubernetes;https - job_name: 'kubernetes-service-endpoints' kubernetes_sd_configs: - role: endpoints relabel_configs: - action: labelmap regex: __meta_kubernetes_service_label_(.+) - source_labels: [__meta_kubernetes_namespace] action: replace target_label: kubernetes_namespace - source_labels: [__meta_kubernetes_service_name] action: replace target_label: kubernetes_name - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape] action: keep regex: true - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme] action: replace target_label: __scheme__ regex: (https?) - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] action: replace target_label: __metrics_path__ regex: (.+) - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port] action: replace target_label: __address__ regex: (.+)(?::\d+);(\d+) replacement: $1:$2 上述Configmap创建了Prometheus配置文件模板。这个配置文件模板将被Thanos sidecar组件读取,它将生成实际的配置文件,而这个配置文件又将被运行在同一个pod中的Prometheus容器所消耗。在配置文件中添加external_labels部分是极其重要的,这样Querier就可以根据这个来重复删除数据。 部署Prometheus Rules configmap 这将创建我们的告警规则,这些规则将被转发到alertmanager,以便发送。 apiVersion: v1 kind: ConfigMap metadata: name: prometheus-rules labels: name: prometheus-rules namespace: monitoring data: alert-rules.yaml: |- groups: - name: Deployment rules: - alert: Deployment at 0 Replicas annotations: summary: Deployment {{$labels.deployment}} in {{$labels.namespace}} is currently having no pods running expr: | sum(kube_deployment_status_replicas{pod_template_hash=""}) by (deployment,namespace) < 1 for: 1m labels: team: devops - alert: HPA Scaling Limited annotations: summary: HPA named {{$labels.hpa}} in {{$labels.namespace}} namespace has reached scaling limited state expr: | (sum(kube_hpa_status_condition{condition="ScalingLimited",status="true"}) by (hpa,namespace)) == 1 for: 1m labels: team: devops - alert: HPA at MaxCapacity annotations: summary: HPA named {{$labels.hpa}} in {{$labels.namespace}} namespace is running at Max Capacity expr: | ((sum(kube_hpa_spec_max_replicas) by (hpa,namespace)) - (sum(kube_hpa_status_current_replicas) by (hpa,namespace))) == 0 for: 1m labels: team: devops - name: Pods rules: - alert: Container restarted annotations: summary: Container named {{$labels.container}} in {{$labels.pod}} in {{$labels.namespace}} was restarted expr: | sum(increase(kube_pod_container_status_restarts_total{namespace!="kube-system",pod_template_hash=""}[1m])) by (pod,namespace,container) > 0 for: 0m labels: team: dev - alert: High Memory Usage of Container annotations: summary: Container named {{$labels.container}} in {{$labels.pod}} in {{$labels.namespace}} is using more than 75% of Memory Limit expr: | ((( sum(container_memory_usage_bytes{image!="",container_name!="POD", namespace!="kube-system"}) by (namespace,container_name,pod_name) / sum(container_spec_memory_limit_bytes{image!="",container_name!="POD",namespace!="kube-system"}) by (namespace,container_name,pod_name) ) * 100 ) < +Inf ) > 75 for: 5m labels: team: dev - alert: High CPU Usage of Container annotations: summary: Container named {{$labels.container}} in {{$labels.pod}} in {{$labels.namespace}} is using more than 75% of CPU Limit expr: | ((sum(irate(container_cpu_usage_seconds_total{image!="",container_name!="POD", namespace!="kube-system"}[30s])) by (namespace,container_name,pod_name) / sum(container_spec_cpu_quota{image!="",container_name!="POD", namespace!="kube-system"} / container_spec_cpu_period{image!="",container_name!="POD", namespace!="kube-system"}) by (namespace,container_name,pod_name) ) * 100) > 75 for: 5m labels: team: dev - name: Nodes rules: - alert: High Node Memory Usage annotations: summary: Node {{$labels.kubernetes_io_hostname}} has more than 80% memory used. Plan Capcity expr: | (sum (container_memory_working_set_bytes{id="/",container_name!="POD"}) by (kubernetes_io_hostname) / sum (machine_memory_bytes{}) by (kubernetes_io_hostname) * 100) > 80 for: 5m labels: team: devops - alert: High Node CPU Usage annotations: summary: Node {{$labels.kubernetes_io_hostname}} has more than 80% allocatable cpu used. Plan Capacity. expr: | (sum(rate(container_cpu_usage_seconds_total{id="/", container_name!="POD"}[1m])) by (kubernetes_io_hostname) / sum(machine_cpu_cores) by (kubernetes_io_hostname) * 100) > 80 for: 5m labels: team: devops - alert: High Node Disk Usage annotations: summary: Node {{$labels.kubernetes_io_hostname}} has more than 85% disk used. Plan Capacity. expr: | (sum(container_fs_usage_bytes{device=~"^/dev/[sv]d[a-z][1-9]$",id="/",container_name!="POD"}) by (kubernetes_io_hostname) / sum(container_fs_limit_bytes{container_name!="POD",device=~"^/dev/[sv]d[a-z][1-9]$",id="/"}) by (kubernetes_io_hostname)) * 100 > 85 for: 5m labels: team: devops 部署Prometheus Stateful Set apiVersion: storage.k8s.io/v1beta1 kind: StorageClass metadata: name: fast namespace: monitoring provisioner: kubernetes.io/gce-pd allowVolumeExpansion: true --- apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: prometheus namespace: monitoring spec: replicas: 3 serviceName: prometheus-service template: metadata: labels: app: prometheus thanos-store-api: "true" spec: serviceAccountName: monitoring containers: - name: prometheus image: prom/prometheus:v2.4.3 args: - "--config.file=/etc/prometheus-shared/prometheus.yaml" - "--storage.tsdb.path=/prometheus/" - "--web.enable-lifecycle" - "--storage.tsdb.no-lockfile" - "--storage.tsdb.min-block-duration=2h" - "--storage.tsdb.max-block-duration=2h" ports: - name: prometheus containerPort: 9090 volumeMounts: - name: prometheus-storage mountPath: /prometheus/ - name: prometheus-config-shared mountPath: /etc/prometheus-shared/ - name: prometheus-rules mountPath: /etc/prometheus/rules - name: thanos image: quay.io/thanos/thanos:v0.8.0 args: - "sidecar" - "--log.level=debug" - "--tsdb.path=/prometheus" - "--prometheus.url=http://127.0.0.1:9090" - "--objstore.config={type: GCS, config: {bucket: prometheus-long-term}}" - "--reloader.config-file=/etc/prometheus/prometheus.yaml.tmpl" - "--reloader.config-envsubst-file=/etc/prometheus-shared/prometheus.yaml" - "--reloader.rule-dir=/etc/prometheus/rules/" env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name : GOOGLE_APPLICATION_CREDENTIALS value: /etc/secret/thanos-gcs-credentials.json ports: - name: http-sidecar containerPort: 10902 - name: grpc containerPort: 10901 livenessProbe: httpGet: port: 10902 path: /-/healthy readinessProbe: httpGet: port: 10902 path: /-/ready volumeMounts: - name: prometheus-storage mountPath: /prometheus - name: prometheus-config-shared mountPath: /etc/prometheus-shared/ - name: prometheus-config mountPath: /etc/prometheus - name: prometheus-rules mountPath: /etc/prometheus/rules - name: thanos-gcs-credentials mountPath: /etc/secret readOnly: false securityContext: fsGroup: 2000 runAsNonRoot: true runAsUser: 1000 volumes: - name: prometheus-config configMap: defaultMode: 420 name: prometheus-server-conf - name: prometheus-config-shared emptyDir: {} - name: prometheus-rules configMap: name: prometheus-rules - name: thanos-gcs-credentials secret: secretName: thanos-gcs-credentials volumeClaimTemplates: - metadata: name: prometheus-storage namespace: monitoring spec: accessModes: [ "ReadWriteOnce" ] storageClassName: fast resources: requests: storage: 20Gi 关于上面提供的manifest,理解以下内容很重要: Prometheus是作为一个有状态集部署的,有3个副本,每个副本动态地提供自己的持久化卷。 Prometheus配置是由Thanos sidecar容器使用我们上面创建的模板文件生成的。 Thanos处理数据压缩,因此我们需要设置--storage.tsdb.min-block-duration=2h和--storage.tsdb.max-block-duration=2h。 Prometheus有状态集被标记为thanos-store-api: true,这样每个pod就会被我们接下来创建的headless service发现。正是这个headless service将被Thanos Querier用来查询所有Prometheus实例的数据。我们还将相同的标签应用于Thanos Store和Thanos Ruler组件,这样它们也会被Querier发现,并可用于查询指标。 GCS bucket credentials路径是使用GOOGLE_APPLICATION_CREDENTIALS环境变量提供的,配置文件是由我们作为前期准备中创建的secret挂载到它上面的。 部署Prometheus服务 apiVersion: v1 kind: Service metadata: name: prometheus-0-service annotations: prometheus.io/scrape: "true" prometheus.io/port: "9090" namespace: monitoring labels: name: prometheus spec: selector: statefulset.kubernetes.io/pod-name: prometheus-0 ports: - name: prometheus port: 8080 targetPort: prometheus --- apiVersion: v1 kind: Service metadata: name: prometheus-1-service annotations: prometheus.io/scrape: "true" prometheus.io/port: "9090" namespace: monitoring labels: name: prometheus spec: selector: statefulset.kubernetes.io/pod-name: prometheus-1 ports: - name: prometheus port: 8080 targetPort: prometheus --- apiVersion: v1 kind: Service metadata: name: prometheus-2-service annotations: prometheus.io/scrape: "true" prometheus.io/port: "9090" namespace: monitoring labels: name: prometheus spec: selector: statefulset.kubernetes.io/pod-name: prometheus-2 ports: - name: prometheus port: 8080 targetPort: prometheus --- #This service creates a srv record for querier to find about store-api's apiVersion: v1 kind: Service metadata: name: thanos-store-gateway namespace: monitoring spec: type: ClusterIP clusterIP: None ports: - name: grpc port: 10901 targetPort: grpc selector: thanos-store-api: "true" 除了上述方法外,你还可以点击这篇文章了解如何在Rancher上快速部署和配置Prometheus服务。 我们为stateful set中的每个Prometheus pod创建了不同的服务,尽管这并不是必要的。这些服务的创建只是为了调试。上文已经解释了 thanos-store-gateway headless service的目的。我们稍后将使用一个 ingress 对象来暴露 Prometheus 服务。 部署Prometheus Querier apiVersion: v1 kind: Namespace metadata: name: monitoring --- apiVersion: apps/v1 kind: Deployment metadata: name: thanos-querier namespace: monitoring labels: app: thanos-querier spec: replicas: 1 selector: matchLabels: app: thanos-querier template: metadata: labels: app: thanos-querier spec: containers: - name: thanos image: quay.io/thanos/thanos:v0.8.0 args: - query - --log.level=debug - --query.replica-label=replica - --store=dnssrv+thanos-store-gateway:10901 ports: - name: http containerPort: 10902 - name: grpc containerPort: 10901 livenessProbe: httpGet: port: http path: /-/healthy readinessProbe: httpGet: port: http path: /-/ready --- apiVersion: v1 kind: Service metadata: labels: app: thanos-querier name: thanos-querier namespace: monitoring spec: ports: - port: 9090 protocol: TCP targetPort: http name: http selector: app: thanos-querier 这是Thanos部署的主要内容之一。请注意以下几点: 容器参数-store=dnssrv+thanos-store-gateway:10901有助于发现所有应查询的指标数据的组件。 thanos-querier服务提供了一个Web接口来运行PromQL查询。它还可以选择在不同的Prometheus集群中去重复删除数据。 这是我们提供Grafana作为所有dashboard的数据源的终点(end point)。 部署Thanos存储网关 apiVersion: v1 kind: Namespace metadata: name: monitoring --- apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: thanos-store-gateway namespace: monitoring labels: app: thanos-store-gateway spec: replicas: 1 selector: matchLabels: app: thanos-store-gateway serviceName: thanos-store-gateway template: metadata: labels: app: thanos-store-gateway thanos-store-api: "true" spec: containers: - name: thanos image: quay.io/thanos/thanos:v0.8.0 args: - "store" - "--log.level=debug" - "--data-dir=/data" - "--objstore.config={type: GCS, config: {bucket: prometheus-long-term}}" - "--index-cache-size=500MB" - "--chunk-pool-size=500MB" env: - name : GOOGLE_APPLICATION_CREDENTIALS value: /etc/secret/thanos-gcs-credentials.json ports: - name: http containerPort: 10902 - name: grpc containerPort: 10901 livenessProbe: httpGet: port: 10902 path: /-/healthy readinessProbe: httpGet: port: 10902 path: /-/ready volumeMounts: - name: thanos-gcs-credentials mountPath: /etc/secret readOnly: false volumes: - name: thanos-gcs-credentials secret: secretName: thanos-gcs-credentials --- 这将创建存储组件,它将从对象存储中向Querier提供指标。 部署Thanos Ruler apiVersion: v1 kind: Namespace metadata: name: monitoring --- apiVersion: v1 kind: ConfigMap metadata: name: thanos-ruler-rules namespace: monitoring data: alert_down_services.rules.yaml: | groups: - name: metamonitoring rules: - alert: PrometheusReplicaDown annotations: message: Prometheus replica in cluster {{$labels.cluster}} has disappeared from Prometheus target discovery. expr: | sum(up{cluster="prometheus-ha", instance=~".*:9090", job="kubernetes-service-endpoints"}) by (job,cluster) < 3 for: 15s labels: severity: critical --- apiVersion: apps/v1beta1 kind: StatefulSet metadata: labels: app: thanos-ruler name: thanos-ruler namespace: monitoring spec: replicas: 1 selector: matchLabels: app: thanos-ruler serviceName: thanos-ruler template: metadata: labels: app: thanos-ruler thanos-store-api: "true" spec: containers: - name: thanos image: quay.io/thanos/thanos:v0.8.0 args: - rule - --log.level=debug - --data-dir=/data - --eval-interval=15s - --rule-file=/etc/thanos-ruler/*.rules.yaml - --alertmanagers.url=http://alertmanager:9093 - --query=thanos-querier:9090 - "--objstore.config={type: GCS, config: {bucket: thanos-ruler}}" - --label=ruler_cluster="prometheus-ha" - --label=replica="$(POD_NAME)" env: - name : GOOGLE_APPLICATION_CREDENTIALS value: /etc/secret/thanos-gcs-credentials.json - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name ports: - name: http containerPort: 10902 - name: grpc containerPort: 10901 livenessProbe: httpGet: port: http path: /-/healthy readinessProbe: httpGet: port: http path: /-/ready volumeMounts: - mountPath: /etc/thanos-ruler name: config - name: thanos-gcs-credentials mountPath: /etc/secret readOnly: false volumes: - configMap: name: thanos-ruler-rules name: config - name: thanos-gcs-credentials secret: secretName: thanos-gcs-credentials --- apiVersion: v1 kind: Service metadata: labels: app: thanos-ruler name: thanos-ruler namespace: monitoring spec: ports: - port: 9090 protocol: TCP targetPort: http name: http selector: app: thanos-ruler 现在,如果你在与我们的工作负载相同的命名空间中启动交互式shell,并尝试查看我们的thanos-store-gateway解析到哪些pods,你会看到以下内容: root@my-shell-95cb5df57-4q6w8:/# nslookup thanos-store-gateway Server: 10.63.240.10 Address: 10.63.240.10#53 Name: thanos-store-gateway.monitoring.svc.cluster.local Address: 10.60.25.2 Name: thanos-store-gateway.monitoring.svc.cluster.local Address: 10.60.25.4 Name: thanos-store-gateway.monitoring.svc.cluster.local Address: 10.60.30.2 Name: thanos-store-gateway.monitoring.svc.cluster.local Address: 10.60.30.8 Name: thanos-store-gateway.monitoring.svc.cluster.local Address: 10.60.31.2 root@my-shell-95cb5df57-4q6w8:/# exit 上面返回的IP对应的是我们的Prometheus Pod、thanos-store和thanos-ruler。这可以被验证为: $ kubectl get pods -o wide -l thanos-store-api="true" NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES prometheus-0 2/2 Running 0 100m 10.60.31.2 gke-demo-1-pool-1-649cbe02-jdnv <none> <none> prometheus-1 2/2 Running 0 14h 10.60.30.2 gke-demo-1-pool-1-7533d618-kxkd <none> <none> prometheus-2 2/2 Running 0 31h 10.60.25.2 gke-demo-1-pool-1-4e9889dd-27gc <none> <none> thanos-ruler-0 1/1 Running 0 100m 10.60.30.8 gke-demo-1-pool-1-7533d618-kxkd <none> <none> thanos-store-gateway-0 1/1 Running 0 14h 10.60.25.4 gke-demo-1-pool-1-4e9889dd-27gc <none> <none> 部署Alertmanager apiVersion: v1 kind: Namespace metadata: name: monitoring --- kind: ConfigMap apiVersion: v1 metadata: name: alertmanager namespace: monitoring data: config.yml: |- global: resolve_timeout: 5m slack_api_url: "<your_slack_hook>" victorops_api_url: "<your_victorops_hook>" templates: - '/etc/alertmanager-templates/*.tmpl' route: group_by: ['alertname', 'cluster', 'service'] group_wait: 10s group_interval: 1m repeat_interval: 5m receiver: default routes: - match: team: devops receiver: devops continue: true - match: team: dev receiver: dev continue: true receivers: - name: 'default' - name: 'devops' victorops_configs: - api_key: '<YOUR_API_KEY>' routing_key: 'devops' message_type: 'CRITICAL' entity_display_name: '{{ .CommonLabels.alertname }}' state_message: 'Alert: {{ .CommonLabels.alertname }}. Summary:{{ .CommonAnnotations.summary }}. RawData: {{ .CommonLabels }}' slack_configs: - channel: '#k8-alerts' send_resolved: true - name: 'dev' victorops_configs: - api_key: '<YOUR_API_KEY>' routing_key: 'dev' message_type: 'CRITICAL' entity_display_name: '{{ .CommonLabels.alertname }}' state_message: 'Alert: {{ .CommonLabels.alertname }}. Summary:{{ .CommonAnnotations.summary }}. RawData: {{ .CommonLabels }}' slack_configs: - channel: '#k8-alerts' send_resolved: true --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: alertmanager namespace: monitoring spec: replicas: 1 selector: matchLabels: app: alertmanager template: metadata: name: alertmanager labels: app: alertmanager spec: containers: - name: alertmanager image: prom/alertmanager:v0.15.3 args: - '--config.file=/etc/alertmanager/config.yml' - '--storage.path=/alertmanager' ports: - name: alertmanager containerPort: 9093 volumeMounts: - name: config-volume mountPath: /etc/alertmanager - name: alertmanager mountPath: /alertmanager volumes: - name: config-volume configMap: name: alertmanager - name: alertmanager emptyDir: {} --- apiVersion: v1 kind: Service metadata: annotations: prometheus.io/scrape: 'true' prometheus.io/path: '/metrics' labels: name: alertmanager name: alertmanager namespace: monitoring spec: selector: app: alertmanager ports: - name: alertmanager protocol: TCP port: 9093 targetPort: 9093 这将创建我们的Alertmanager部署,它将根据Prometheus规则生成所有告警。 部署Kubestate指标 apiVersion: v1 kind: Namespace metadata: name: monitoring --- apiVersion: rbac.authorization.k8s.io/v1 # kubernetes versions before 1.8.0 should use rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: kube-state-metrics roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: kube-state-metrics subjects: - kind: ServiceAccount name: kube-state-metrics namespace: monitoring --- apiVersion: rbac.authorization.k8s.io/v1 # kubernetes versions before 1.8.0 should use rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: name: kube-state-metrics rules: - apiGroups: [""] resources: - configmaps - secrets - nodes - pods - services - resourcequotas - replicationcontrollers - limitranges - persistentvolumeclaims - persistentvolumes - namespaces - endpoints verbs: ["list", "watch"] - apiGroups: ["extensions"] resources: - daemonsets - deployments - replicasets verbs: ["list", "watch"] - apiGroups: ["apps"] resources: - statefulsets verbs: ["list", "watch"] - apiGroups: ["batch"] resources: - cronjobs - jobs verbs: ["list", "watch"] - apiGroups: ["autoscaling"] resources: - horizontalpodautoscalers verbs: ["list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 # kubernetes versions before 1.8.0 should use rbac.authorization.k8s.io/v1beta1 kind: RoleBinding metadata: name: kube-state-metrics namespace: monitoring roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: kube-state-metrics-resizer subjects: - kind: ServiceAccount name: kube-state-metrics namespace: monitoring --- apiVersion: rbac.authorization.k8s.io/v1 # kubernetes versions before 1.8.0 should use rbac.authorization.k8s.io/v1beta1 kind: Role metadata: namespace: monitoring name: kube-state-metrics-resizer rules: - apiGroups: [""] resources: - pods verbs: ["get"] - apiGroups: ["extensions"] resources: - deployments resourceNames: ["kube-state-metrics"] verbs: ["get", "update"] --- apiVersion: v1 kind: ServiceAccount metadata: name: kube-state-metrics namespace: monitoring --- apiVersion: apps/v1 kind: Deployment metadata: name: kube-state-metrics namespace: monitoring spec: selector: matchLabels: k8s-app: kube-state-metrics replicas: 1 template: metadata: labels: k8s-app: kube-state-metrics spec: serviceAccountName: kube-state-metrics containers: - name: kube-state-metrics image: quay.io/mxinden/kube-state-metrics:v1.4.0-gzip.3 ports: - name: http-metrics containerPort: 8080 - name: telemetry containerPort: 8081 readinessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 timeoutSeconds: 5 - name: addon-resizer image: k8s.gcr.io/addon-resizer:1.8.3 resources: limits: cpu: 150m memory: 50Mi requests: cpu: 150m memory: 50Mi env: - name: MY_POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: MY_POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace command: - /pod_nanny - --container=kube-state-metrics - --cpu=100m - --extra-cpu=1m - --memory=100Mi - --extra-memory=2Mi - --threshold=5 - --deployment=kube-state-metrics --- apiVersion: v1 kind: Service metadata: name: kube-state-metrics namespace: monitoring labels: k8s-app: kube-state-metrics annotations: prometheus.io/scrape: 'true' spec: ports: - name: http-metrics port: 8080 targetPort: http-metrics protocol: TCP - name: telemetry port: 8081 targetPort: telemetry protocol: TCP selector: k8s-app: kube-state-metrics Kubestate指标部署需要转发一些重要的容器指标,这些指标不是kubelet原生暴露的,因此不能直接提供给Prometheus。 部署Node-Exporter Daemonset apiVersion: v1 kind: Namespace metadata: name: monitoring --- apiVersion: extensions/v1beta1 kind: DaemonSet metadata: name: node-exporter namespace: monitoring labels: name: node-exporter spec: template: metadata: labels: name: node-exporter annotations: prometheus.io/scrape: "true" prometheus.io/port: "9100" spec: hostPID: true hostIPC: true hostNetwork: true containers: - name: node-exporter image: prom/node-exporter:v0.16.0 securityContext: privileged: true args: - --path.procfs=/host/proc - --path.sysfs=/host/sys ports: - containerPort: 9100 protocol: TCP resources: limits: cpu: 100m memory: 100Mi requests: cpu: 10m memory: 100Mi volumeMounts: - name: dev mountPath: /host/dev - name: proc mountPath: /host/proc - name: sys mountPath: /host/sys - name: rootfs mountPath: /rootfs volumes: - name: proc hostPath: path: /proc - name: dev hostPath: path: /dev - name: sys hostPath: path: /sys - name: rootfs hostPath: path: / Node-Exporter daemonset在每个节点上运行一个node-exporter的pod,并暴露出非常重要的节点相关指标,这些指标可以被Prometheus实例拉取。 部署Grafana apiVersion: v1 kind: Namespace metadata: name: monitoring --- apiVersion: storage.k8s.io/v1beta1 kind: StorageClass metadata: name: fast namespace: monitoring provisioner: kubernetes.io/gce-pd allowVolumeExpansion: true --- apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: grafana namespace: monitoring spec: replicas: 1 serviceName: grafana template: metadata: labels: task: monitoring k8s-app: grafana spec: containers: - name: grafana image: k8s.gcr.io/heapster-grafana-amd64:v5.0.4 ports: - containerPort: 3000 protocol: TCP volumeMounts: - mountPath: /etc/ssl/certs name: ca-certificates readOnly: true - mountPath: /var name: grafana-storage env: - name: GF_SERVER_HTTP_PORT value: "3000" # The following env variables are required to make Grafana accessible via # the kubernetes api-server proxy. On production clusters, we recommend # removing these env variables, setup auth for grafana, and expose the grafana # service using a LoadBalancer or a public IP. - name: GF_AUTH_BASIC_ENABLED value: "false" - name: GF_AUTH_ANONYMOUS_ENABLED value: "true" - name: GF_AUTH_ANONYMOUS_ORG_ROLE value: Admin - name: GF_SERVER_ROOT_URL # If you're only using the API Server proxy, set this value instead: # value: /api/v1/namespaces/kube-system/services/monitoring-grafana/proxy value: / volumes: - name: ca-certificates hostPath: path: /etc/ssl/certs volumeClaimTemplates: - metadata: name: grafana-storage namespace: monitoring spec: accessModes: [ "ReadWriteOnce" ] storageClassName: fast resources: requests: storage: 5Gi --- apiVersion: v1 kind: Service metadata: labels: kubernetes.io/cluster-service: 'true' kubernetes.io/name: grafana name: grafana namespace: monitoring spec: ports: - port: 3000 targetPort: 3000 selector: k8s-app: grafana 这将创建我们的Grafana部署和服务,它将使用我们的Ingress对象暴露。为了做到这一点,我们应该添加Thanos-Querier作为我们Grafana部署的数据源: 点击添加数据源 设置Name: DS_PROMETHEUS 设置Type: Prometheus 设置URL: http://thanos-querier:9090 保存并测试。现在你可以构建你的自定义dashboard或从grafana.net简单导入dashboard。Dashboard #315和#1471都非常适合入门。 部署Ingress对象 apiVersion: extensions/v1beta1 kind: Ingress metadata: name: monitoring-ingress namespace: monitoring annotations: kubernetes.io/ingress.class: "nginx" spec: rules: - host: grafana.<yourdomain>.com http: paths: - path: / backend: serviceName: grafana servicePort: 3000 - host: prometheus-0.<yourdomain>.com http: paths: - path: / backend: serviceName: prometheus-0-service servicePort: 8080 - host: prometheus-1.<yourdomain>.com http: paths: - path: / backend: serviceName: prometheus-1-service servicePort: 8080 - host: prometheus-2.<yourdomain>.com http: paths: - path: / backend: serviceName: prometheus-2-service servicePort: 8080 - host: alertmanager.<yourdomain>.com http: paths: - path: / backend: serviceName: alertmanager servicePort: 9093 - host: thanos-querier.<yourdomain>.com http: paths: - path: / backend: serviceName: thanos-querier servicePort: 9090 - host: thanos-ruler.<yourdomain>.com http: paths: - path: / backend: serviceName: thanos-ruler servicePort: 9090 这是拼图的最后一块。有助于将我们的所有服务暴露在Kubernetes集群之外,并帮助我们访问它们。确保将<yourdomain>替换为一个你可以访问的域名,并且你可以将Ingress-Controller的服务指向这个域名。 现在你应该可以访问Thanos Querier,网址是:http://thanos-querier.<yourdomain>.com。它如下所示: 确保选中重复数据删除(deduplication)。 如果你点击Store,可以看到所有由thanos-store-gateway服务发现的活动端点。 现在你可以在Grafana中添加Thanos Querier作为数据源,并开始创建dashboard。 Kubernetes集群监控dashboard Kubernetes节点监控dashboard 总 结 将Thanos与Prometheus集成在一起,无疑提供了横向扩展Prometheus的能力,而且由于Thanos-Querier能够从其他querier实例中提取指标数据,因此实际上你可以跨集群提取指标数据,并在一个单一的仪表板中可视化。 我们还能够将指标数据归档在对象存储中,为我们的监控系统提供无限的存储空间,同时从对象存储本身提供指标数据。这种设置的主要成本部分可以归结为对象存储(S3或GCS)。如果我们对它们应用适当的保留策略,可以进一步降低成本。 然而,实现这一切需要你进行大量的配置。上面提供的manifest已经在生产环境中进行了测试,你可以大胆进行尝试。

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

在采用K8S之前您必须了解的5件事情

云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 作者简介 | Christopher Tozzi,自2008年来以自由职业者的身份对Linux、虚拟化、容器、数据存储及其相关主题进行报道。 时至今日,Kubernetes已然成为风靡一时的容器编排调度工具,许多IT分析师均提出了企业应当在何时采用Kubernetes的深刻建议。然而,和所有其他的软件平台一样,Kubernetes并非是一个适用于所有人的灵丹妙药。我更倾向于认为人们在有些时候过分夸大了Kubernetes的作用,以至于他们产生了一种错觉:他们无法离开Kubernetes,而实际上,Kubernetes比他们真正的需求要复杂得多。 为了分析人们真正的需求与Kubernetes的匹配程度,我分析了企业在采用Kubernetes编排之前必须考虑的5个事情。 Kubernetes是什么? 如果您关注容器,您可能会知道Kubernetes是一个用于容器编排的开源工具,它可以自动执行诸如启动容器、停止容器以及在同一个容器的不同实例之间的负载均衡等重要任务。 简而言之,Kubernetes的主要目的是最大限度地减少工程师必须手动执行的管理工作量,并通过简化容器操作,帮助企业大规模运行复杂的容器化应用程序。 决定是否采用Kubernetes的关键要素 基于Kubernetes的设立初衷,如果您喜欢自动化,讨厌手动执行重复性的任务,那么Kubernetes无疑是您的极佳选择。 这是您决定是否采用Kubernetes的重要前提,但是,您不能仅根据这一“前提”就决定是否采用Kubernetes。在采用Kubernetes之前,您还需要考虑并权衡其他重要的因素。 1、Kubernetes的基础设施规模 您的基础设施规模是其中一个决定Kubernetes是否能够很好地为您所用的关键要素。 Kubernetes的设计初衷是协调分布在真正庞大的环境中的容器,这往往意味着企业应当拥有数十台主机服务器。根据过往的实施经验,如果基础架构中的服务器少于50个,那么您可能没有足够的资源来利用Kubernetes的全部优势。 这并不是指Kubernetes无法在较小规模的基础设施上运行。实际上,如果您愿意,您可以在单个主机上运行Kubernetes。然而,由于Kubernetes其中的一个研发目的是:通过在数量庞大的集群中分布容器化应用程序提供高可用性,因此,如果您只有少量服务器,则无法享受到Kubernetes的某些价值。 除此之外,考虑到设置和维护Kubernetes的复杂性,如果您的基础设施规模较小,无法完全实现Kubernetes的高可用性承诺,那么或许您不应投入过多时间和精力在Kubernetes上。 对于较小的基础架构,您可以使用较为简单的容器编排工具,或者使用如AWS ECS等具有内置编排的基于云的容器服务。 2、Kubernetes操作系统环境 Kubernetes主要是一种Linux技术。尽管Kubernetes可以用于管理托管Windows服务器上的容器化应用程序,这些应用程序作为Kubernetes服务器集群内的所谓工作节点运行。但托管Kubernetes核心服务的主要服务器或者说主节点必须是Linux。 因此,如果您的商店以Windows为中心,那么Kubernetes并非您的最佳选择。但是您可以选择Rancher轻松将Kubernetes的优势引入Windows,并且极大程度降低使用的复杂性。 3、安装和设置Kubernetes 在决定采用Kubernetes之前,您还需要评估您可以在此项目上投入的工作时间。 普通的开放源代码版本的Kubernetes缺少内置的应用程序,也并未提供一种可以适用于所有默认配置的安装方式。在集群正常运行之前,您需要投入大量的时间从头开始编写及调整配置文件。因此,安装和配置Kubernetes的过程或许是一个令人生畏的过程,您需要投入大量的时间和精力。 部分Kubernetes发行版提供了交互式安装程序脚本,可以帮助您自动执行大部分设置过程。如果您选择Rancher等Kubernetes发行版,则有望在一两天内轻松完成配置及安装。 第三种选择是使用诸如Google Kubernetes Engine等云供应商解决方案,将Kubernetes作为托管服务在云上运行。在这种情况下,您可以自行选择安装及设置。但值得注意的一点是,在确定如何配置Kubernetes环境时,您的选择可能会受到限制。 您必须意识到最为关键的一点:不要低估配置Kubernetes的难度。在您真的要全身心投入Kubernetes之前,请确保您所付出的努力是值得的。另一方面,如果您无法确定为企业在生产集群上安装和部署Kubernetes的难度,您可以尝试使用K3s等轻量级Kubernetes发行版来进行测试,预估后续需要付出多少努力来进行Kubernetes的配置和设置。 4、Kubernetes和声明式配置管理 Kubernetes采用了所谓的声明式配置管理方法,这就意味着,您需要自行编写配置文件来设置Kubernetes应用程序应当如何运行,而Kubernetes将自动指出如何使应用程序符合规范。 声明式配置管理与命令式配置管理相反,在命令式配置管理中,您可以自行配置应用程序的每个组件,并让其按照您所想要的方式运行。 声明式配置是Kubernetes在许多用户实例中如此强大和可伸缩的其中一个原因。您可以设置一次配置,并且根据需要多次应用它。 但是,如果您的配置需求不断变化,或者在工作负载或环境中的不同部分之间变化,那么您应当如何处理呢?在这种情况下,声明式配置管理将成为一个障碍,您将发现自己需要不断地调整先前认为是“一劳永逸”的配置文件。 因此,在您选择采用Kubernetes之前,您需要考虑应用程序的配置需求。只有当您所需要的配置相对通用且静态时,Kubernetes才是一个不错的选项。 5、Kubernetes和多云 Rancher等部分Kubernetes发行版的主要功能之一,是单个Kubernetes部署可以编排多个集群,无论集群位于在不同的公有云还是私有云上。这一功能使Kubernetes成为协助控制多云架构复杂性的优秀工具。 在跨多云部署容器化应用程序,并且Kubernetes的设置和配置工作很合理时,多云上的Kubernetes是十分有意义的。 在这一因素中,您需要留意的是,在考虑是否以及何时采用Kubernetes时,应考虑您当前的多云战略以及多云扩展计划。 结 语 Kubernetes是一个非常棒的工具,在正确设置的情况下,它可以产生巨大的价值。但是,它并没有达到杀手级应用程序的状态,因为它无法在所有用户实例中交付价值。在您被巨大的宣传攻势攻陷,并确定您无法离开Kubernetes之前,请清醒地对自己的需求进行评估,明确Kubernetes是否能在真正意义上帮助您更加有效、更加可靠地运行应用程序。 【云栖号在线课堂】每天都有产品技术专家分享!课程地址:https://yqh.aliyun.com/live 立即加入社群,与专家面对面,及时了解课程最新动态!【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK 原文发布时间:2020-06-24本文作者:hristopher Tozz本文来自:“dockone”,了解相关信息可以关注“dockone”

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

更新应用时,如何实现 K8s 零中断滚动更新?

作者 | 子白(阿里云开发工程师)、溪恒(阿里云技术专家) <关注阿里巴巴云原生公众号,回复排查即可下载电子书> 《深入浅出 Kubernetes》一书共汇集 12 篇技术文章,帮助你一次搞懂 6 个核心原理,吃透基础理论,一次学会 6 个典型问题的华丽操作! Kubernetes集群中,业务通常采用Deployment + LoadBalancer类型Service的方式对外提供服务,其典型部署架构如图1所示。这种架构部署和运维都十分简单方便,但是在应用更新或者升级时可能会存在服务中断,引发线上问题。今天我们来详细分析下这种架构为何在更新应用时会发生服务中断以及如何避免服务中断。 图1 业务部署图 为何会发生服务中断 Deployment滚动更新时会先创建新pod,等待新pod running后再删除旧pod。 新建Pod 图2 服务中断示意图 中断原因:Pod running后被加入到Endpoint后端,容器服务监控到Endpoint变更后将Node加入到SLB后端。此时请求从SLB转发到Pod中,但是Pod业务代码还未初始化完毕,无法处理请求,导致服务中断,如图2所示。 解决方法:为pod配置就绪检测,等待业务代码初始化完毕后后再将node加入到SLB后端。 删除Pod 在删除旧pod过程中需要对多个对象(如Endpoint、ipvs/iptables、SLB)进行状态同步,并且这些同步操作是异步执行的,整体同步流程如图3所示。 图3 Deployment更新时序图 Pod pod状态变更:将Pod设置为Terminating状态,并从所有Service的Endpoints列表中删除。此时,Pod停止获得新的流量,但在Pod中运行的容器不会受到影响; 执行preStop Hook:Pod删除时会触发preStop Hook,preStop Hook支持bash脚本、TCP或HTTP请求; 发送SIGTERM 信号:向Pod中的容器发送SIGTERM信号; 等待指定的时间:terminationGracePeriodSeconds字段用于控制等待时间,默认值为30秒。该步骤与preStop Hook同时执行,因此terminationGracePeriodSeconds需要大于preStop的时间,否则会出现preStop未执行完毕,pod就被kill的情况; 发送SIGKILL信号:等待指定时间后,向pod中的容器发送SIGKILL信号,删除pod。 中断原因:上述1、2、3、4步骤同时进行,因此有可能存在Pod收到SIGTERM信号并且停止工作后,还未从Endpoints中移除的情况。此时,请求从slb转发到pod中,而Pod已经停止工作,因此会出现服务中断,如图4所示。 图4 服务中断示意图 解决方法:为pod配置preStop Hook,使Pod收到SIGTERM时sleep一段时间而不是立刻停止工作,从而确保从SLB转发的流量还可以继续被Pod处理。 iptables/ipvs 中断原因:当pod变为termintaing状态时,会从所有service的endpoint中移除该pod。kube-proxy会清理对应的iptables/ipvs条目。而容器服务watch到endpoint变化后,会调用slb openapi移除后端,此操作会耗费几秒。由于这两个操作是同时进行,因此有可能存在节点上的iptables/ipvs条目已经被清理,但是节点还未从slb移除的情况。此时,流量从slb流入,而节点上已经没有对应的iptables/ipvs规则导致服务中断,如图5所示。 图5 服务中断示意图 解决方法: Cluster 模式:Cluster模式下kube-proxy会把所有业务Pod写入Node的iptables/ipvs中,如果当前Node没有业务pod,则该请求会被转发给其他Node,因此不会存在服务中断,如6所示; 图6 Cluster模式请求转发示意图 Local模式:Local模式下,kube-proxy仅会把Node上的pod写入iptables/ipvs。当Node上只有一个pod且状态变为terminating时,iptables/ipvs会将该pod记录移除。此时请求转发到这个node时,无对应的iptables/ipvs记录,导致请求失败。这个问题可以通过原地升级来避免,即保证更新过程中Node上至少有一个Running Pod。原地升级可以保障Node的iptables/ipvs中总会有一条业务pod记录,因此不会产生服务中断,如图7所示; 图7 Local模式原地升级时请求转发示意图 ENI 模式 Service:ENI模式绕过kube-proxy,将Pod直接挂载到SLB后端,因此不存在因为iptables/ipvs导致的服务中断。 图 8 ENI模式请求转发示意图 SLB 图 9服务中断示意图 中断原因:容器服务监控到Endpoints变化后,会将Node从slb后端移除。当节点从slb后端移除后,SLB对于继续发往该节点的长连接会直接断开,导致服务中断。 解决方法:为SLB设置长链接优雅中断(依赖具体云厂商)。 如何避免服务中断 避免服务中断可以从Pod和Service两类资源入手,接下来将针对上述中断原因介绍相应的配置方法。 Pod配置 apiVersion: v1 kind: Pod metadata: name: nginx namespace: default spec: containers: - name: nginx image: nginx # 存活检测 livenessProbe: failureThreshold: 3 initialDelaySeconds: 30 periodSeconds: 30 successThreshold: 1 tcpSocket: port: 5084 timeoutSeconds: 1 # 就绪检测 readinessProbe: failureThreshold: 3 initialDelaySeconds: 30 periodSeconds: 30 successThreshold: 1 tcpSocket: port: 5084 timeoutSeconds: 1 # 优雅退出 lifecycle: preStop: exec: command: - sleep - 30 terminationGracePeriodSeconds: 60 注意:需要合理设置就绪检测(readinessProbe)的探测频率、延时时间、不健康阈值等数据,部分应用启动时间本身较长,如果设置的时间过短,会导致 POD 反复重启。 livenessProbe为存活检测,如果失败次数到达阈值(failureThreshold)后,pod会重启,具体配置见官方文档; readinessProbe为就绪检查,只有就绪检查通过后,pod才会被加入到Endpoint中。容器服务监控到Endpoint变化后才会将node挂载到slb后端; preStop时间建议设置为业务处理完所有剩余请求所需的时间,terminationGracePeriodSeconds时间建议设置为preStop的时间再加30秒以上。 Service配置 Cluster模式(externalTrafficPolicy: Cluster) apiVersion: v1 kind: Service metadata: name: nginx namespace: default spec: externalTrafficPolicy: Cluster ports: - port: 80 protocol: TCP targetPort: 80 selector: run: nginx type: LoadBalancer 容器服务会将集群中所有节点挂载到SLB的后端(使用BackendLabel标签配置后端的除外),因此会快速消耗SLB quota。SLB限制了每个ECS上能够挂载的SLB的个数,默认值为50,当quota消耗完后会导致无法创建新的监听及SLB。 Cluster模式下,如果当前节点没有业务pod会将请求转发给其他Node。在跨节点转发时需要做NAT,因此会丢失源IP。 Local模式(externalTrafficPolicy: Local) apiVersion: v1 kind: Service metadata: name: nginx namespace: default spec: externalTrafficPolicy: Local ports: - port: 80 protocol: TCP targetPort: 80 selector: run: nginx type: LoadBalancer # 需要尽可能的让每个节点在更新的过程中有至少一个的Running的Pod # 通过修改UpdateStrategy和利用nodeAffinity尽可能的保证在原地rolling update # * UpdateStrategy可以设置Max Unavailable为0,保证有新的Pod启动后才停止之前的pod # * 先对固定的几个节点打上label用来调度 # * 使用nodeAffinity+和超过相关node数量的replicas数量保证尽可能在原地建新的Pod # 例如: apiVersion: apps/v1 kind: Deployment ...... strategy: rollingUpdate: maxSurge: 50% maxUnavailable: 0% type: RollingUpdate ...... affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: deploy operator: In values: - nginx 容器服务默认会将Service对应的Pod所在的节点加入到SLB后端,因此SLB quota消耗较慢。Local模式下请求直接转发到pod所在node,不存在跨节点转发,因此可以保留源IP地址。Local模式下可以通过原地升级的方式避免服务中断,yaml文件如上。 ENI模式(阿里云特有模式) apiVersion: v1 kind: Service metadata: annotations: service.beta.kubernetes.io/backend-type: "eni" name: nginx spec: ports: - name: http port: 30080 protocol: TCP targetPort: 80 selector: app: nginx type: LoadBalancer Terway网络模式下,通过设置service.beta.kubernetes.io/backend-type: "eni" annotation可以创建ENI模式的SLB。ENI模式下,pod会直接挂载到SLB后端,不经过kube-proxy,因此不存在服务中断的问题。请求直接转发到pod,因此可以保留源IP地址。 三种svc模式对比如下表所示。 图 10 Service对比 结论 Terway 网络模式 (推荐方式) 选用ENI模式的svc + 设定Pod优雅终止 + 就绪检测。 Flannel 网络模式 如果集群中slb数量不多且不需要保留源ip:选用cluster模式 + 设定Pod优雅终止 + 就绪检测; 如果集群中slb数量较多或需要保留源ip:选用local模式 + 设定Pod优雅终止 + 就绪检测 + 原地升级(保证更新过程中每个节点上至少有一个Running Pod)。 Reference 容器生命周期钩子 Configure Liveness, Readiness and Startup Probes 通过负载均衡访问服务 Kubernetes 最佳实践:优雅的中止 Kubernetes社区相关讨论:Create ability to do zero downtime deployments when using externalTrafficPolicy: Local,Graceful Termination for External Traffic Policy Local 容器服务 kubernetes(ACK)中应用优雅上下线 课程推荐 为了更多开发者能够享受到 Serverless 带来的红利,这一次,我们集结了 10+ 位阿里巴巴 Serverless 领域技术专家,打造出最适合开发者入门的 Serverless 公开课,让你即学即用,轻松拥抱云计算的新范式——Serverless。 点击即可免费观看课程:https://developer.aliyun.com/learning/roadmap/serverless “阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

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

从零开始入门 K8s:理解容器运行时接口 CRI

云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! CRI 是 Kubernetes 体系中跟容器打交道的一个非常重要的部分。本文作者主要分为三个部分来进行:首先会为大家介绍 CRI 接口的一个由来和它的设计;其次会和大家分享目前有哪些 CRI 的实现;最后会给大家介绍一下相关的工具有哪些。 一、CRI 介绍 在 CRI 出现之前(也就是 Kubernetes v1.5 之前),Docker 作为第一个容器运行时,Kubelet 通过内嵌的 dockershim 操作 Docker API 来操作容器,进而达到一个面向终态的效果。在这之后,又出现了一种新的容器运行时 - rkt,它也想要成为 Kubernetes 支持的一个容器运行时,当时它也合到了 Kubelet 的代码之中。这两个容器运行时的加入使得 Kubernetes 的代码越来越复杂、难以维护。之后 hyber.sh 加入社区,也想成为第三个容器运行时。 此时就有人站出来说,我们能不能对容器运行时的操作抽象出一个接口,将 Kubelet 代码与具体的容器运行时的实现代码解耦开,只要实现了这样一套接口,就能接入到 Kubernetes 的体系中,这就是我们后来见到的 Container Runtime Interface (CRI)。 有一句话说得很好,「软件问题都可以通过加一层来解决」,我们的 CRI 就是加了这样一层。CRI 接口的通信协议是 gRPC,这里的一个时代背景就是当时的 gRPC 刚刚开源,此外它的性能也是优于 http/REST 模式的。gRPC 不需要手写客户端代码和服务端代码,能够自动生成通信协议代码。 接下来我们介绍一下 CRI 接口的设计。 二、CRI 实现 CRI 接口 在引入了 CRI 接口之后,Kubelet 的架构如上图所示。 跟容器最相关的一个 Manager 是 Generic Runtime Manager,就是一个通用的运行时管理器。我们可以看到目前 dockershim 还是存在于 Kubelet 的代码中的,它是当前性能最稳定的一个容器运行时的实现。remote 指的就是 CRI 接口。CRI 接口主要包含两个部分: 一个是 CRI Server,即通用的比如说创建、删除容器这样的接口; 另外一个是流式数据的接口 Streaming Server,比如 exec、port-forward 这些流式数据的接口。 这里需要注意的是,我们的 CNI(容器网络接口)也是在 CRI 进行操作的,因为我们在创建 Pod 的时候需要同时创建网络资源然后注入到 Pod 中。接下来就是我们的容器和镜像。我们通过具体的容器创建引擎来创建一个具体的容器。 给大家介绍一下 CRI 接口的设计。我们知道 Kubernetes 的一个运作的机制是面向终态的,在每一次调协的循环中,Kubelet 会向 apiserver 获取调度到本 Node 的 Pod 的数据,再做一个面向终态的处理,以达到我们预期的状态。 循环的第一步,首先通过 List 接口拿到容器的状态,再通过 Sandbox 和 Container 接口来创建容器,另外还有镜像接口用来拉取容器镜像。CRI 描述了 Kubelet 期望的容器运行时行为,主要就是我们刚刚所说的 3 个部分。 通过 CRI 操作容器的生命周期 比方说我们通过 kubectl 命令来运行一个 Pod,那么 Kubelet 就会通过 CRI 执行以下操作: 首先调用 RunPodSandbox 接口来创建一个 Pod 容器,Pod 容器是用来持有容器的相关资源的,比如说网络空间、PID 空间、进程空间等资源; 然后调用 CreatContainer 接口在 Pod 容器的空间创建业务容器; 再调用 StartContainer 接口启动容器,相对应的销毁容器的接口为 StopContainer 与 RemoveContainer。 CRI streaming 接口 这里给大家介绍一下 CRI 的流式接口 exec。它可以用来在容器内部执行一个命令,又或者说可以 attach 到容器的 IO 流中做各种交互式的命令。它的特别之处在于,一个是节省资源,另一个是连接的可靠性。 首先 exec 操作会发送到 apiserver,经过鉴权,apiserver 将对 Kubelet Server 发起 exec 的请求,然后 Kubelet 会调用 CRI 的 exec 接口将具体的请求发至容器的运行时。这个时候,容器运行时不是直接地在 exec 接口上来服务这次请求,而是通过我们的 streaming server 来异步地返回每一次执行的结果。也就是说 apiserver 其实实际上是跟 streaming server 交互来获取我们的流式数据的。这样一来让我们的整个 CRI Server 接口更轻量、更可靠。 CRI 的一些实现目前 CRI 的一些实现: CRI-containerd CRI-O PouchContainer @alibaba … CRI-containerd 是目前社区中比较主流的新一代 CRI 的实现,CRI-O 来自于红帽公司,PouchContainer 是由 alibaba 实现的 CRI,其它的 CRI 实现,这里就不一一介绍了。 CRI-containerd下图是 CRI-containerd 的架构。 这套 CRI 接口是基于 containerd 实现的。在早期的实现中,CRI 其实是作为一个独立进程的,再跟 containerd 进行交互。这样一来又多了一层进程跟进程之间的开销,因此在后来的版本中 CRI 的是直接以插件的形式实现到 containerd 中的,成为了 containerd 的一部分,从而能够可插拔地使用 CRI 接口。 整个架构看起来非常直观。这里的 Meta services、Runtime service 与 Storage service 都是 containerd 提供的接口。它们是通用的容器相关的接口,包括镜像管理、容器运行时管理等。CRI 在这之上包装了一个 gRPC 的服务。右侧就是具体的容器的实现,比如说,创建容器时就要创建具体的 runtime 和它的 shim,它们和 Container 一起组成了一个 Pod Sandbox。 CRI-containerd 的一个好处是,containerd 还额外实现了更丰富的容器接口,所以它可以用 containerd 提供的 ctr 工具来调用这些丰富的容器运行时接口,而不只是 CRI 接口。 CRI-O下图是 CRI-O 的实现思路。 它是通过直接在 OCI 上包装容器接口来实现的一个 CRI 服务。它对外提供的只有具体的 CRI 接口,没有我们前面所提到的 containerd 提供的更丰富的接口。它主要包含两个部分,首先是对容器 runtime 的管理,另一个是对镜像的管理。 三、相关工具 下面给大家介绍一下 CRI 相关的工具。这几个工具都在特别兴趣小组的一个项目里面。 crictl 它是一个类似 docker 的命令行工具,用来操作 CRI 接口。它能够帮助用户和开发者调试容器问题,而不是通过 apply 一个 yaml 到 apiserver、再通过 Kubelet 操作的方式来调试。这样的链路太长,而这个命令行工具可以直接操作 CRI。 critest 用于验证 CRI 接口行为是否是符合预期的。 性能工具 还有一些性能工具用来测试接口性能。 四、思考时间 1.目前 CRI 接口处于 v1 alpha2 版本,CRI 规范能不能更完善? CRI 标准的制定是至上而下的,通过 Kubernetes 的一些 feature 反向地要求 CRI 提供这样的功能,进而完善 CRI 规范。 2.如何通过 annotation 方式自定义 runtime 行为? 我们目前的 CRI 肯定不能满足所有用户的需求,很多公司可能会对 CRI 接口做一些增强、定制,比如说 alibaba。最简单的方式是通过 annotation 来自定义 runtime 的行为。在每个接口都设置一个 annotation 的字段,容器运行时通过理解这些字段来去自定义 runtime 的行为。同学们可以尝试去在各个 CRI 接口中通过识别 annotation 的方式来达到自定义 runtime 行为的目的。 五、本节总结 本节课的主要内容就到此为止了,这里为大家简单总结一下: CRI 介绍:CRI 的出现是为了将容器运行时与 Kubernetes 解耦开; CRI 实现:CRI-O 与 CRI-containerd; CRI 工具:CRI 调试工具 cri-tools, CRI 测试工具 critest。 【云栖号在线课堂】每天都有产品技术专家分享!课程地址:https://yqh.aliyun.com/zhibo 立即加入社群,与专家面对面,及时了解课程最新动态!【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK 原文发布时间:2020-03-29本文作者:知谨本文来自:“InfoQ”,了解相关信息可以关注“InfoQ ”

资源下载

更多资源
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等操作系统。

用户登录
用户注册