kube-scheduler中的cache介绍

介绍 首先我们来了解下Cache的作用:收集pod信息,并提供节点级的聚合信息。其设计目标是为通用调度器提供高效查询。Cache的操作是以pod为中心的,能够基于pod的事件进行增量更新。但是由于事件是通过网络发送的,无法保证所有事件都能够传递,因为使用了Reflector进行list和watch操作,可能会由于延迟或relist出现事件丢失的情况。具体参考pkg/scheduler/internal/cache/interface.go中的说明。 在Cache中有有一个assume的概念,其含义为假定、假设,代表了调度器认为这个pod是已调度状态,这个是在scheduleCycle找到合适的节点之后设置的,但是此时还没有调用bind操作将其绑定到具体的节点上,在运行完绑定之后会将其从assumedPods中移除。由于bind的是异步执行的,所以调度器在启动这个协程之后会开始一个新的调度周期来调度下一个pod,在计算已用资源时,是会包含在assume集合中的pod的。 Cache中的数据时基于事件触发添加和更新的,因此为了保证在一个调度周期执行过程中,用到的数据不会发生变化,保证数据的一致性,scheduler在调度开始会使用当前Cache生成一个节点信息的快照,后续会用这个快照进行计算。 接口 下面首先看下Cache的接口定义,了解一下有哪些方法: type Cache interface { // 节点数量 // 测试用 NodeCount() int // pod数量 // 测试用 PodCount() (int, error) // 假设pod已调度,并聚合pod的信息到其对应的节点上,加入到assumedPods。 // 调度框架使用 AssumePod(pod *v1.Pod) error // 完成绑定,设置assumedPod过期时间,后面在执行清理时会在assumedPods中删掉。 // 调度框架使用 FinishBinding(pod *v1.Pod) error // 删除assumedPod。 // 调度框架使用 ForgetPod(pod *v1.Pod) error // 添加pod,如果是assumedPod,则会执行确认逻辑,否则则是过期的pod,将其加入到缓存。 // 已调度pod informer使用 AddPod(pod *v1.Pod) error // 更新pod信息 // 已调度pod informer使用 UpdatePod(oldPod, newPod *v1.Pod) error // 从缓存中删除pod // 已调度pod informer使用 RemovePod(pod *v1.Pod) error //…

runc init流程分析

runc init的作用是创建和配置容器并启动容器内进程。在执行runc create或runc exec等命令时,都会通过fork自己(/proc/self/exec)的方式创建出一个容器的parent进程,该parent进一步创建出child进程,而child进程再进一步创建出grandchild进程,在不同进程内会分别做不同的配置工作,这些进程之间通过环境变量和管道进行通信和交互。…

runc命令行工具基本使用

runc是linux上一个用来运行容器的管理工具,遵循OCI规范。 首先,我们通过一些简单的例子来演示一下runc创建和启动容器的例子: mkdir /tmp/mycontainer cd /tmp/mycontainer # 创建容器文件系统 mkdir rootfs # 导出busybox系统目录到rootfs docker export $(docker create busybox) | tar -C rootfs -xvf - 下面来生成一个spec文件:config.json。runc提供一个一个spec命令帮助我们生成一个基础的模板,后续我们可以基于这个模板作出自定义的修改。 ruc spec 启动容器: root@workstation:/tmp/testrunc# runc run mycontainer / # ip a 1: lo: <LOOPBACK,UP,LOWER_UPmtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever / # hostname runc /…

cgroup模式检测

cgroup有v1和v2版本,系统上对cgroupfs的挂载支持三种不同的模式: 统一(Unified) — 最简单的方式,只暴露纯粹的cgroup v2接口。在该模式下,/sys/fs/cgroup 是唯一挂载到主机上的文件系统,所有的控制器都通过它暴露。 兼容(Legacy) — 传统的cgroup v1模式,该模式下不同的控制器拥有自己挂载目录:/sys/fs/cgroup/<controller>/。在此基础之上,systemd拥有自己的cgroup层级来实现管理功能:/sys/fs/cgroup/systemd/。 混合(Hybrid) — 这是上面两种模式的结合形式。这种模式和兼容模式非常相似,但是会多挂载一个层级目录/sys/fs/cgroup/unified/,该目录下包含cgoup v2的接口(但是该目录下只是暴露cgroupfs层级树,不包含控制器功能,控制器都挂载到了兼容模式的单独目录下,/sys/fs/cgroup/unified/仅仅是纯粹的cgroupfs的v2的功能能力,无关资源管控)。在此种模式下,既能够兼容cgroup v1的功能,又能使用一些cgroup v2的特性。 对于不同的操作系统发行版,或者相同发行版的不同版本,挂载模式可能都不一样,因此要使用cgroup功能,需要使用者(如runc,新版jvm)能够检测当前的系统上使用的是何种模式,对于检测何种模式可以使用下列简单的方式: 在根命名空间中,可以使用statfs()获取/sys/fs/cgroup/这个文件系统的类型,如果.f_type是 CGROUP2_SUPER_MAGIC,则说明是统一模式。如果.f_type是TMPFS_MAGIC ,则可能是cgroup v1模式或者可能是混合模式,更进一步检查可通过statfs()获取/sys/fs/cgroup/unified/的文件系统类型,如果它的.f_type是CGROUP2_SUPER_MAGIC,则说明使用的是混合模式,否则就是cgroup v1模式。 下面是containerd中的实现: // github.com/containerd/cgroups/utils.go // Mode returns the cgroups mode running on the host func Mode() CGMode { checkMode.Do(func() { var st unix.Statfs_t if err := unix.Statfs(unifiedMountpoint, &st); err != nil { cgMode = Unavailable return } switch st.Type { case unix.CGROUP2_SUPER_MAGIC: cgMode = Unified default: cgMode = Legacy if err := unix.Statfs(filepath.Join(unifiedMountpoint,…

kube-prometheus问题小计

最近再Kubernetes集群上部署kube-prometheus套件遇到了一点小问题,再次记录一下。 按照官方的快速指引部署到集群中后,发现没有部署custom-metrics和external-metrics服务,而正好需要使用这两个功能做自动扩缩容,因此按照该文档进行自定义部署,但是这里遇到了一些小问题。 首先按照文档中的指引,安装jb,jsonnet和gojsontoyaml这几个命令: # 此处静态编译为了拿到任何地方都能直接用 yum install -y glibc-devel go install -a -ldflags='-linkmode external -extldflags -static' github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb@latest go install -a github.com/google/go-jsonnet/cmd/jsonnet@latest go install -a github.com/brancz/gojsontoyaml@latest mkdir bin/ cp ~/go/bin/{gojsontoyaml,jsonnet,jb} bin/ export PATH=`pwd`/bin:$PATH jb init # Creates the initial/empty `jsonnetfile.json` jb install github.com/prometheus-operator/kube-prometheus/jsonnet/kube-prometheus@release-0.9 # jb update # 下载对应版本的build.sh和example.jsonnet文件 $ wget https://raw.githubusercontent.com/prometheus-operator/kube-prometheus/release-0.9/example.jsonnet -O example.jsonnet $ wget https://raw.githubusercontent.com/prometheus-operator/kube-prometheus/release-0.9/build.sh -O build.sh # 编辑example.jsonnet,去掉被注释掉的custom metrics和external metrics部分 sh build.sh example.jsonnet # 上面命令会重新生成manifests目录 kubectl apply -f manifests/setup…

external-dns介绍和基本使用

external-dns用来将集群内的pod,service,ingress等资源注册到外面的dns上,这样集群外的服务可通过统一的dns访问到集群内的服务,external-dns支持将域名注册到不同的域名服务,如aws,linode,或coredns,bind-dns等。external-dns支持众多kubernetes资源到外部域名服务的注册发布,具体支持的资源类型可进入source目录查看,同时也支持众多的外部域名服务支持,具体可进入provider目录查看。 项目主页:https://github.com/kubernetes-sigs/external-dns 本文主要使用bind作为外部dns服务进行演示测试,将集群内的service,ingress,和istio-gateway,istio-virtualservice注册到外面的dns上。 域名划分 首先我们需要对域名做一个划分: svc.mydomain.com kubernetes集群内服务域名,此处我们集群内外使用相同的域 hosts.mydomain.com 主机节点的dns解析域 mydomain.com 基础服务 部署bind bind的主配置文件修改了本地监听的ip地址,注释掉了ipv6的监听,使用tsig-keygen -a hmac-sha256 externaldns命令生成了认证密匙并配置到了配置文件中,加入forwarders转发配置,此处要求配置为客户基础环境中的。 [root@vm10-77-1-87 ~]# cat /etc/named.conf // // named.conf // // Provided by Red Hat bind package to configure the ISC BIND named(8) DNS // server as a caching only nameserver (as a localhost DNS resolver only). // // See /usr/share/doc/bind*/sample/ for example named configuration files. // // See the BIND Administrator's Reference Manual (ARM) for…

探索client-go中的exec和cp的原理

开发kubernetes控制器中可能涉及到进入特定的container中执行命令的操作,或者拷贝文件等操作,此时我们可能会借助client-go提供的这个工具去实现: "k8s.io/client-go/tools/remotecommand" // 构建请求 req := client.RESTClient().Post(). Resource("pods"). Name(podName). Namespace(namespace). SubResource("exec") req.VersionedParams(&v1.PodExecOptions{ Container: containerName, Command: cmd, Stdin: false, Stdout: true, Stderr: true, TTY: true, }, scheme.ParameterCodec) // 创建执行器 exec, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL()) // 执行命令 exec.Stream(remotecommand.StreamOptions{ Stdin: nil, Stdout: &buf, Stderr: &buf, Tty: true, }) 上面代码执行时会向apiserver发起一个请求,apiserver会通过代理机制将请求转发给相应节点上的kubelet服务,kubelet会通过cri接口调用runtime的接口发起流式接口中的Exec()接口进入到container执行。针对一般化的命令调用,输入参数和输出结果多数以文本为主,并不会占用带宽和apiserver的资源,但是当设计到文件拷贝的时候则会占用较多带宽,因此在实际使用时这种方式拷贝大文件来说可能并不是最优方案。 此处顺便描述一下如kubectl工具对cp命令的原理,其实也是调用的exec接口,然后执行tar命令进行文件的拷贝动作: # 从容器向外拷贝,通过tar命令压缩将输出重定向到标准输出 tar cf - <srcFile# 从外面向容器内拷贝,将标准输入数据通过tar解压写入到container内的文件目录 tar --no-same-permissions --no-same-owner -xmf - # 或 tar -xmf - 考虑到上面所说的带宽问题,在实际使用时也可向kubelet直接发起exec请求,只要通过client-go将pod所在节点实现查询一下即可,当然还会设计一些端口,证书问题,下面是一个直接向kubelet发起请求的示例代码。 package main import…

kubernetes基础 – 管理守护进程

damonset一般运行在集群的所有节点上,是一些常驻服务,通常可以用来收集日志,作为存储节点,运行监控进程等。 下面是一个日志收集的daemonset的定义: apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-elasticsearch namespace: kube-system labels: k8s-app: fluentd-logging spec: selector: matchLabels: name: fluentd-elasticsearch updateStrategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 maxSurge: "100%" template: metadata: labels: name: fluentd-elasticsearch spec: # nodeSelector: # node-role.kubernetes.io/worker: "true" tolerations: - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule containers: - name: fluentd-elasticsearch image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2 resources: limits: memory: 200Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers…

kubernetes基础 – 管理有状态服务

statefulset区别于deployment有两个最重要的特性:有序性,固定性。 适用的场景 StatefulSets 对于需要满足以下一个或多个需求的应用程序很有价值: 稳定的、唯一的网络标识符 稳定的、持久的存储 有序的、优雅的部署和缩放 有序的、自动的滚动更新 限制 pod的持久化存储基于pvc和pv。 为保证数据安全,删除或者收缩statefulset并不会自动删除它关联的存储卷。 需要手动创建无头服务使pod的dns可解析。 删除 statefulset时,并不按照有序性执行,要求有序的话,可以在删除之前将 statefulset 缩放为 0。 默认pod管理策略(OrderedReady) 时使用滚动更新,有可能会有更新终止,需要人为修复的状况。 有序性: 每个pod都有一个序号 部署时和扩容时根据序号递增依次启动新pod 缩容时依次根据序号递减依次删除pod 固定标识 statefulset在创建pod的时候会为每个pod分配下面几个固定的标识。 固定网络标识 pod的名称和pod的主机名是根据statefulset的名称和当前pod的序号组成的:$(statefulset-name)-$(ordinal)。dns解析需要无头服务。 规则 Cluster Domain Service (ns/name) StatefulSet (ns/name) StatefulSet Domain Pod DNS Pod Hostname cluster.local default/nginx default/web nginx.default.svc.cluster.local web-{0..N-1}.nginx.default.svc.cluster.local web-{0..N-1} cluster.local foo/nginx foo/web nginx.foo.svc.cluster.local web-{0..N-1}.nginx.foo.svc.cluster.local web-{0..N-1} kube.local foo/nginx foo/web nginx.foo.svc.kube.local web-{0..N-1}.nginx.foo.svc.kube.local web-{0..N-1} 固定存储 pvc名称规则:$(tpl-metadata.name)-$(podname) pod标签 每个pod标签里会自动加一个 statefulset.kubernetes.io/pod-name 方便针对单个pod创建service。 pod管理策略 对于部署,扩缩容和更新时的并行度设置。 OrderedReady(依次) Parallel(并行,只影响扩缩操作,对更新策略不生效) 更新策略 手动(需要手动删除pod)…

kubernetes基础 – 管理无状态服务

考虑到性能和可用性,通常一个服务会有多个服务实例,在k8s中通过多个pod实现。 无状态服务是各个pod完全是一样,彼此可替代。 有状态服务是各个pod不完全一样,不可彼此替代,比如pod关联特定存储卷。 对于以下场景可以使用replicaset: 稳定的、唯一的网络标识符。 稳定的、持久的存储。 有序的、优雅的部署和缩放。 有序的、自动的滚动更新。 replicaset讲解 replicaset 的目的是维护一组在任何时候都处于运行状态的 Pod 副本的稳定集合。 因此,它通常用来保证给定数量的、完全相同的 Pod 的可用性。 replicaset通过pod的metadata.ownerReferences对应的replicaset。如果匹配的pod没有这个值扩不是控制器,那么replicaset将捕获纳管这个pod。 ReplicaSet 确保任何时间都有指定数量的 Pod 副本在运行。 然而,Deployment 是一个更高级的概念,它管理ReplicaSet,并向 Pod 提供声明式的更新以及许多其他有用的功能。 因此,我们建议使用 Deployment 而不是接使用 ReplicaSet,除非 你需要自定义更新业务流程或根本不需要更新。 示例replicaset: apiVersion: apps/v1 kind: ReplicaSet metadata: name: frontend labels: app: guestbook tier: frontend spec: # modify replicas according to your case replicas: 3 selector: matchLabels: tier: frontend template: metadata: labels: tier: frontend spec: containers: - name: php-redis image: gcr.io/google_samples/gb-frontend:v3 操作:…