吐核|Core Dump

笔记 随想 吐槽


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

k8s调度器优化之步长+有限个数node的调度

发表于 2019-03-11

当k8s的node数量变大的时候,k8s调度器中的2个调度环节会变的很长,可能会造成总体超过秒级别,这显然是不可接受的,所以我们会对调度的环节进行优化。
比如我们对findNodesThatFit进行优化
这个函数真正干活的一行代码workqueue.Parallelize(16, len(nodes), checkNode)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Parallelize is a very simple framework that allow for parallelizing
// N independent pieces of work.
//以下代码就是实现了启动workers个协程并发运行,然后从toProcess里面取数字,丢给doWorkPiece运行
func Parallelize(workers, pieces int, doWorkPiece DoWorkPieceFunc) {
toProcess := make(chan int, pieces)
for i := 0; i < pieces; i++ {
toProcess <- i
}
close(toProcess)

if pieces < workers {
workers = pieces
}

wg := sync.WaitGroup{}
wg.Add(workers)
for i := 0; i < workers; i++ {
go func() {
defer utilruntime.HandleCrash()
defer wg.Done()
for piece := range toProcess {
doWorkPiece(piece)
}
}()
}
wg.Wait()
}

那么其实我们就可以有个最大想法就是我们其实没用必要遍历所有的节点,而可以设计成遍历部分节点,并且能找到我们需要的个数个的节点停止就可以了,对这个函数进行改造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

// ParallelizeUntil is a framework that allows for parallelizing N
// independent pieces of work until done or the context is canceled.
//当ctx被取消的时候我们就停止,然后可以在doWorkPiece中实现当找到对的node的数量到我们设定的值的时候就取消
func ParallelizeUntil(ctx context.Context, workers, pieces int, doWorkPiece DoWorkPieceFunc) {
var stop <-chan struct{}
if ctx != nil {
stop = ctx.Done()
}

toProcess := make(chan int, pieces)
for i := 0; i < pieces; i++ {
toProcess <- i
}
close(toProcess)

if pieces < workers {
workers = pieces
}

wg := sync.WaitGroup{}
wg.Add(workers)
for i := 0; i < workers; i++ {
go func() {
defer utilruntime.HandleCrash()
defer wg.Done()
for piece := range toProcess {
select {
case <-stop: //注意这里
return
default:
doWorkPiece(piece)
}
}
}()
}
wg.Wait()
}

并且修改checkNode中

1
2
3
4
5
6
7
length := atomic.AddInt32(&filteredLen, 1)
if length > numNodesToFind {
cancel() //这里cancel掉
atomic.AddInt32(&filteredLen, -1)
} else {
filtered[length-1] = g.cachedNodeInfoMap[nodeName].Node()
}

以上功能,其实在官方实现之前就实现了,同事们没有提交PR,看到官方也有这个功能了,那么可以对外写写这个东西。
至于我的标题步长的是什么意思呢,其实关注了下社区的PR,发现之前也有类似的实现过程中的问题,我们的实现是先找一定数量的机器,找到满足数量个的之后就退出,否则继续找下个步长个的机器。官方其实也经历了类似的实现过程,不过最终改成了上面的代码,巧妙的利用了context,去掉了要分步长的逻辑,值得学习.

我想象中的理想的k8s-paas平台方案

发表于 2019-03-07 | 更新于 2019-03-11

“k8s现在发展越来越成熟了,大家可以思考一下,如果让你来设计基于k8s的PaaS平台,要怎么做?k8s是作为PaaS的内核。”

今天组内leader提了个这个问题,刚好这块之前也有些想法,尤其是这些年在百度的Noah,京东云的云翼,商城的JDOS都有过相关维护也有过从零开发的研发经历,也作为业务方深度使用过相关的PAAS平台(Matrix JPAAS ORP ORCP等),看过各种不同的思路,加上我自己的想法和抉择,打算记录下,形成一个比较好的实践说明,后面不断补充。

PAAS我觉得至少要有3块

  1. CMDB(机器管理+实例管理+权限管理)
  2. 部署系统(编译构建+分级) 扩展加上包/镜像管理,编排上线等
  3. 监控系统(物理监控+业务监控) 扩展出趋势图,故障分析等
    以及一些周边系统比如网络和域名管理(负载均衡和域名),安全管理(堡垒机+操作审计),作业平台等

CMDB

首推Noah服务树的模式,资料可以参考这里第八页(目前业界多家互联网公司都用了类似的模式)。简单来说就是以树形结构展示资源信息,每个叶子节点下是具体的机器或者实例列表,每个节点上都可以加入配置信息。

配置管理上:

一般会根据公司组织架构划分1 2级节点,或者按运维团队划分。要求公司管理的所有机器都在树上可查,可快速查到机器的归属关系,配置信息等等。除了界面入口外,一般会提供命令行工具给运维团队快速查询节点和机器的相互对应关系,如查询某个节点a.b.c 下的机器列表,或者查询某个机器的归属节点等。并且界面上也有方便的导入导出工具,减少目前部分团队用excel来管理机器的简陋模式,也能方便IDC,安全等团队,查询机器归属时使用,避免不必要的误操作。

权限管理上:

每个节点上都可以设置权限,权限有继承关系,即有上级节点权限的人有权限操作下级节点,以及下级节点上的人员设置,这个后续会和登陆系统(门神),堡垒机系统等打通。即有权限的同学就可以登陆自己的机器,会根据设置的角色,控制权限,比如只有只读账户权限,还是可以sudo其他账户的权限,这里还可以根据公司要求设置审批流.

部署系统

部署系统目前这个时间点来看,可以分为非镜像的部署(k8s出现前的情况),以及镜像的部署。这2套系统应该是相辅相成的,不应该只有一套系统,另外一套系统没有用同样的用户体验来实现。

阅读全文 »

如何实现系列二:实现一个自己的admission controller

发表于 2019-03-06 | 更新于 2019-03-11

准入控制器(Admission Controller)位于 API Server 中,在对象被持久化之前,准入控制器拦截对 API Server 的请求,一般用来做身份验证和授权,不过从设计上由于位于apiserver把数据写入etcd之前,所以我们可以加入一些自己的逻辑,或者改写apisever到etcd中的数据。线上我们长开的admission主要是NamespaceLifecycle,ResourceQuota,namespacelifecycle是保证删namespace的时候不会再有pod创建;resourcequota主要是总控namespace下的request和limit,一般我们用来做总体资源限制,其他admission的具体介绍可以参考官方文档
https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/

admission的各个策略的实现很容器找到,代码都在中src/k8s.io/kubernetes/plugin/pkg/admission 中,
不过admission本身的代码实现就有点难找了,藏在了src/k8s.io/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer.go 中,
一般ide还不一定能找到。

阅读全文 »

如何实现系列一:如何控制k8s pod中磁盘空间的大小

发表于 2019-03-06 | 更新于 2019-03-11

这2年基于k8s做了大量的工作,其中很多相信业界的同仁也会碰到,打算开个系列来介绍下,相关的东西如何实现,也作为这方面的一些总结

k8s集群管理必然会遇到的一个问题,磁盘空间控制问题, 业务方的代码跑在容器中,必然会生成日志和临时文件,也必然会遇到磁盘空间控制的问题,不然业务方就直接把磁盘用满了。
如果google搜索下的话基本的解决方案是:修改docker的参数 ,添加 –storage-opt dm.basesize= 参数设置为需要的值 😀

然而这不是一个可以随不同的业务和容器变化的值,即docker daemon启动后,创建的容器的根分区的大小都是这个值。并不满足我们的需求,我们需要的是根据不同的业务设置不同的大小,即我们提供给用户的容器规格除了CPU和MEM之外还有DISK的大小,当然dm.basesize这个值推荐修改(这个值也限制了镜像的大小),默认值是10G,建议修改成20G,虽然目前大部分业务镜像都是在5G之内,但是随着机器学习的兴起,相关的镜像普遍偏大,有超过10G点现象

回到正题,如何限制磁盘空间:
首先我们的容器用的磁盘都是本地盘,那么就需要从本地磁盘的方案来考虑限制。即需要有办法从本地磁盘切分不同的大小:这里推荐2个方法:LVM(lvm相关命令)以及DeviceMapper(dmsetup相关命令)。这2个方案分别在不同的地方实践过,各有利弊。即我们通过一些系统命令,在node上的物理磁盘上分配出相应大小的空间,然后通过flexvolume挂到容器中,实现控制容器磁盘大小的目的(注意这里根分区还是basesize的大小)。FlexVolume的功能请参考前文

阅读全文 »

k8s中的GPU共享功能实现分析

发表于 2019-02-21 | 更新于 2019-03-11

GPU在容器间共享这个功能其实都有需要,但是官方都没有,之前在公司内实现的改动也太过于in-tree,无法opensource,今天看到阿里云开源了个GPU共享的方案,那么顺道借分析下阿里的方案来总结下这方面的事情。

阿里云的代码在
https://github.com/AliyunContainerService/gpushare-scheduler-extender
https://github.com/AliyunContainerService/gpushare-device-plugin
阿里云实现了一个gpushare的device-plugin,然后实现了相关的调度逻辑

由于具体的共享逻辑在node上,所以我们先看下device-plugin的实现。
之前看过nvidia的官方device-plugin实现,本质很简单,需要实现2个接口 1.listandwatch用于获得机器上的device列表,2. allocate接口用于kubelet分配设备时,这个设备要做的一些事情 这个设计其实不是那么好,这个之前的nvidia k8s和官方的区别中 有提到,限制了deviceplugin对设备的调度控制能力 (device-plugin相关的实现后面也看下补篇文章)

入口代码,和nvidia官方本质没什么区别,加载了nvml的库(cgo的)用户获取机器上的gpu的信息
创建了个fsNotify去实现deviceplugin要求的相关逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
func (ngm *sharedGPUManager) Run() error {
log.V(1).Infoln("Loading NVML")
if err := nvml.Init(); err != nil {
log.V(1).Infof("Failed to initialize NVML: %s.", err)
log.V(1).Infof("If this is a GPU node, did you set the docker default runtime to `nvidia`?")
select {}
}
defer func() { log.V(1).Infoln("Shutdown of NVML returned:", nvml.Shutdown()) }()
log.V(1).Infoln("Fetching devices.")
if getDeviceCount() == uint(0) {
log.V(1).Infoln("No devices found. Waiting indefinitely.")
select {}
}
log.V(1).Infoln("Starting FS watcher.")
watcher, err := newFSWatcher(pluginapi.DevicePluginPath)
if err != nil {
log.V(1).Infoln("Failed to created FS watcher.")
return err
}
defer watcher.Close()
log.V(1).Infoln("Starting OS watcher.")
sigs := newOSWatcher(syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
restart := true
var devicePlugin *NvidiaDevicePlugin
L:
for {
if restart {
if devicePlugin != nil {
devicePlugin.Stop()
}
devicePlugin = NewNvidiaDevicePlugin(ngm.enableMPS, ngm.healthCheck)
if err := devicePlugin.Serve(); err != nil {
log.Warningf("Failed to start device plugin due to %v", err)
} else {
restart = false
}
}
select {
case event := <-watcher.Events:
if event.Name == pluginapi.KubeletSocket && event.Op&fsnotify.Create == fsnotify.Create {
log.V(1).Infof("inotify: %s created, restarting.", pluginapi.KubeletSocket)
restart = true
}

case err := <-watcher.Errors:
log.Warningf("inotify: %s", err)

case s := <-sigs:
switch s {
case syscall.SIGHUP:
log.V(1).Infoln("Received SIGHUP, restarting.")
restart = true
case syscall.SIGQUIT:
t := time.Now()
timestamp := fmt.Sprint(t.Format("20060102150405"))
log.Infoln("generate core dump")
coredump("/etc/kubernetes/go_" + timestamp + ".txt")
default:
log.V(1).Infof("Received signal \"%v\", shutting down.", s)
devicePlugin.Stop()
break L
}
}
}

return nil
}

阅读全文 »

nvidia kubernetes 和官方k8s的不同

发表于 2019-02-19 | 更新于 2019-03-11

nvidia kubernetes是从官方一个commit 拉出来改的,所有的改动可以通过下面的链接看到
https://github.com/NVIDIA/kubernetes/compare/dd5e1a2978fd0b97d9b78e1564398aeea7e7fe92...NVIDIA:master

主要的几个改动

  1. 添加了扩展资源的定义,对container上的ExtendedResourceRequest,对podSpec上的extendedResources,selector上的extendedResourceAffinity,node上的ExtendedResources
    https://github.com/NVIDIA/kubernetes/commit/355aff2ffc31d6403c78b68d94fa263b0de35188

  2. 重写了个Device Plugin API,去除了原来的register流程,加了adminPod initContainer接口,实现了对node上添加资源的接口定义

    1
    2
    3
    4
    5
    6
    7
     struct Device {
    ID: "GPU-fef8089b-4820-abfc-e83e-94318197576e",
    State: "Healthy",
    Attributes: {
    "memory": "8000",
    }
    }

    看意思是插件注册到某一个目录下就自动发现了,然后减少了注册的流程,有点像flexvolume插件的感觉,总体的改动感觉不错,原来的流程需要看下,我记得原来的流程分配卡的逻辑是kubelet里面实现的,感觉不太好,这里虽然好像也没改,但是加入了插件可以修改podSpec和containerSpec的逻辑
    https://github.com/NVIDIA/kubernetes/commit/23d735f02d39fd1c1464e67a355b64642cecfec6?diff=split

  3. 加了个docker hook的功能,看代码的意思是在/usr/share/containers/docker/hooks.d/目录下可以写个json,json里面可以填

    1
    2
    3
    4
    5
    type dockerHook struct {
    Runtime string `json:"runtime"`
    Annotations map[string]string `json:"annotations"`
    Images []string `json:"images"`
    }

    则在docker容器运行的时候可以判断容器上有没有annotation或者image是符合hook中所写的,如果是则启动容器的时候用json中所写的runtime来启动,另外加了个默认会看的annotation annotation.io.kubernetes.container.runtime

  4. 调度器上实现了对于extendResource的调度功能
    https://github.com/NVIDIA/kubernetes/commit/7e06c18139e44d45bd2c0c2f702dc31716f34fe2
    https://github.com/NVIDIA/kubernetes/commit/3cebd40083d69e0f65a5040fe4537e74f5a4e529
    感觉不是特别优雅,最好还是把这个资源算在普通资源调度里面比较好,现在实现是每个函数都多加了入参

  5. 困了没细看 https://github.com/NVIDIA/kubernetes/commit/df14bc461de5f639d6fd0d1eb6b7450b5a2a816b 感觉是重构了下

https://docs.google.com/document/d/1ZgKH_K4SEfdiE_OfxQ836s4yQWxZfSjS288Tq9YIWCA/edit#heading=h.56cb5eq5qgcy

k8s的flexvolume-plugin介绍

发表于 2019-02-02 | 更新于 2019-03-11

k8s的卷管理目前都是in-tree的,也就是写在k8s代码中,实际看了下对国内用户唯一有价值的就ceph相关的cephfs和rbd以及glusterfs,其他基本都不是国内用户在用的。
除此之外我们可以扩展的就flexvolomeplugin或者是csi,csi需要高版本的k8s以及有点麻烦,这里就不关注了。还是主要关注flexvolumeplugin

flexvolume有多个用处:

  1. 用于区分本地的磁盘,分出特定的大小,挂载到容器中,用于动态控制用户可用的空间,而不依赖于docker的配置
  2. 在有client的情况下,挂载远端的(分布式)文件系统

我这里的用途主要是用于挂载分布式存储,在实现插件的过程中,顺道看下flexvolume的相关代码
首先需要了解的是:我们需要实现一个可执行程序放在/usr/libexec/kubernetes/kubelet-plugins/volume/exec/<vendor~driver>/ 这样的目录, 实现以下接口
init:kubelet/kube-controller-manager 初始化存储插件时调用,插件需要返回是否需要要 attach 和 detach 操作
attach:将存储卷挂载到 Node 上
detach:将存储卷从 Node 上卸载
waitforattach: 等待 attach 操作成功(超时时间为 10 分钟)
isattached:检查存储卷是否已经挂载
mountdevice:将设备挂载到指定目录中以便后续 bind mount 使用
unmountdevice:将设备取消挂载
mount:将存储卷挂载到指定目录中
umount:将存储卷取消挂载
具体的可以看官方设计文档https://github.com/kubernetes/community/blob/master/contributors/devel/sig-storage/flexvolume.md

阅读全文 »

lvm相关知识以及与docker配合使用记录

发表于 2018-12-28 | 更新于 2019-03-11

LVM基础知识

LVM现在用的还是挺多的,每次看每次每次忘,还是记录下吧

LVM3个概念,PV,VG,LV 递进式的,一层层往上

LVM

如图所示:

  1. PV可以创建在一块硬盘 pvcreate /dev/sdb 或者一个分区上 pvcreate /dev/sdb1
  2. VG创建在PV之上,VG可以跨多个PV创建 vgcreate docker /dev/sdb /dev/sdc
  3. LV创建在VG之中,LV不可以跨VG, lvcreate -L ${SIZE} -n ${VOLUMEID} ${VG} ,可以-L指定大小创建,也可以lvcreate –wipesignatures y -n thinpool docker -l 95%VG 按百分比的方式创建

日常命令:
pvs,vgs,lvs 一类命令,我一般用来看空间大小以及还剩空间
vgs可以看到剩余的空间,表示lv分不出去了就占了那么大,不能超卖
vgdisplay docker –units g | grep ‘VG Size’ | awk ‘{print $3}’ | cut -d. -f1 看docker这个vg的大小

阅读全文 »

简单限制用户可执行命令来做堡垒机的方案

发表于 2018-12-27 | 更新于 2019-03-11

今天有朋友问怎么限制用户能执行的命令来做跳板机,看了下有好几种,这里记录下

  1. 将/etc/passwd相应用户的最后一列换成自己的一个脚本,脚本里面限制用户只能输入IP,然后脚本去自动ssh
    可以useradd -s xxx username的方式来修改,不过我一般喜欢直接vim
    脚本可以参考
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    while [1];do
    echo -n "Input User@IP: "
    read input
    case "$input" in
    q|quit|QUIT|Q)
    exit
    ;;
    *)
    ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$input"
    ;;
    esac
    done
阅读全文 »

k8s上rbac功能实践

发表于 2018-12-26 | 更新于 2019-03-11

上一篇已经讲到了 ABAC,这篇讲下 RBAC,RBAC 现在是 k8s 官方推荐的做权限限制的方案,ABAC 已经不再做功能增加了

官方文档写的有点复杂,这里简化描述下,RBAC 首先有2个概念:

  • 一个叫 Role(或者是ClusterRole),Role 里面定义了可以干什么事情,有什么样的权限,Role 定义的资源只能是某个既定 namespace 下的,而ClusterRole 可以定义 cluster 相关的资源,比如对namespace这个资源本身的权限。
  • 另外一个概念叫 RoleBinding(ClusterRoleBinding) 是一个把用户和 ROLE 关联起来的东西,即这个用户有这个 Role 里面定义的权限. PS用户一般是 –basic-auth-file 里面定义的用户,当然也有些系统默认有的账户,比如 system:unsecure 表示走http访问没有认证的。

具体的例子:

阅读全文 »

123…6

lovejoy

今天不开心

56 日志
12 分类
13 标签
Github Twitter
© 2019 lovejoy
由 Hexo 强力驱动 v3.8.0
|
主题 – NexT.Pisces v7.0.1