kubernetes基于virtual-kubelet实现弹性Pod

说明

简介

  • virtual-kubelet 是一个开源的社区主导型项目,是Kubernetes kubelet的一种实现
  • 它伪装成kubelet,与Kubernetes集群API通信
  • 实现Kubernetes API向阿里云的ECI、AWS的Fargate等serverless平台扩展
  • Github项目地址virtual-kubelet
  • 默认看此文的都具备一定的能力,抄作业不会把名字都抄了

架构

工作原理

  • 一般情况下,kubelet为每个node节点实现Pod的操作,所有操作都基于本地的计算资源。
  • Virtual-kubelet在Kubernetes API看来,跟普通的kubelet API差不多,区别在于可以在其他地方调度容器。
  • 某种程度上来讲,可以把Virtual-kubelet理解为一个功能受限的、资源近乎无限的node节点
  • 所有的Pod并不会跑在一个集中式的“真实”节点上,而是被打散到云平台资源池里面。
  • 为了实现kubelet API,Virtual-kubelet提供插件式的Provider接口,允许开发者去根据自身情况自定义的实现普通kubelet功能
  • 这里简单罗列一下公有云的Provider实现,更多的Provider实现,看项目文档

跟普通kubelet区别

由于Virtual-kubelet需要考虑安全性问题,并不会实现所有kubelet功能,例如特权容器,因此没法挂载宿主机的docker

这里以阿里云的ECI为例

不支持的功能具体内容推荐方案
HostPathMount本地宿主机文件到容器中使用emptyDir,或者NAS存储
HostNetwork将宿主机端口映射到容器中使用type=LoadBalancer的负载均衡
DaemonSet在容器所在宿主机上部署static pod通过sidecar形式在pod中部署多个镜像
Privileged权限容器拥有privileged权限使用securityContext为pod添加Capability
type=NodePort的Service通过宿主机端口映射到容器端口使用type=LoadBalancer的负载均衡

架构图

Virtual-Kubelet架构图

项目架构图

阿里云ECI架构图

阿里云ECI架构图

使用场景

  • 业务容器弹性扩容,用在集群资源不足时,弹性扩展到Virtual-kubelet
  • 业务容器完全托管
  • 数据处理任务
  • CI/CD任务
  • 定时任务

优势

对于很多 Kubernetes 集群,通常同时支撑在线和离线多种负载,在线负载流量的波动性和离线计算任务的时间不确定性,导致在不同时刻负载的资源需求呈波峰波谷状,比如很多企业需要在周末、月中和月末进行大批量的数据计算,在特定的时间点需要大量的计算力,以应对突发的计算资源需求。

目前k8s通常的做法是通过autoscaler自动扩容节点(约3 min启动一个新节点),直到pod被成功调度运行,当pod执行完成后会自动回收临时节点。这种扩容方式pod往往需要等待 > 3 min才能被调度运行。

通过 Virtual kubelet ,可以用最小的运维成本(无需调整节点数量),来应对集群计算资源高峰压力。

简单演示

这里基于阿里云的ACK进行演示,以Nginx作为容器镜像

阿里云ACK对接ECI

参考此文档

注意事项

  • 阿里云的virtual-kubelet节点默认是带上了污点virtual-kubelet.io/provider=alibabacloud:NoSchedule
  • 确保一般情况下,Pod不会被调度Virtual-kubelet节点

QuickStart

  • 指定nodeName,跳过k8s调度,手动调度到Virtual-kubelet节点
  • 定义resources字段,只指定容器CPU和内存规格,ECI会尝试使用多种ECS规格进行支撑。
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
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
nodeName: virtual-node-eci-0
containers:
- image: nginx:1.18
name: nginx
resources:
limits:
cpu: 250m
memory: 512Mi
requests:
cpu: 250m
memory: 512Mi
  • 通过annotations,指定使用ECS规格
    • 实例内容器可以不用限制资源上限
    • 容器resource定义可以不用指定request和limit
    • 各容器可以最大程度的共享申请的资源。
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
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
annotations:
# 只指定CPU和内存,会自动匹配多个同规格的ECS
k8s.aliyun.com/eci-use-specs : "2-4Gi"
# 指定ECS具体的规格,多个实例规格用逗号隔开
k8s.aliyun.com/eci-use-specs : "ecs.c5.large,ecs.c6.large"
labels:
app: nginx
spec:
nodeName: virtual-node-eci-0
containers:
- image: nginx:1.18
name: nginx
resources:
limits:
cpu: 250m
memory: 512Mi
requests:
cpu: 250m
memory: 512Mi
  • 通过annotations,指定使用SPOT实例,限制出价
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
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
annotations:
k8s.aliyun.com/eci-use-specs : "ecs.c5.large" #根据需要替换您接受的 ECS 规格
k8s.aliyun.com/eci-spot-strategy: "SpotWithPriceLimit" #最高价限定策略
k8s.aliyun.com/eci-spot-price-limit: "0.250" #小时最高单价
labels:
app: nginx
spec:
nodeName: virtual-node-eci-0
containers:
- image: nginx:1.18
name: nginx
resources:
limits:
cpu: 250m
memory: 512Mi
requests:
cpu: 250m
memory: 512Mi

小试牛刀

场景介绍

  • 假定jenkins的master节点已经有权限访问Kubernetes API增删改查Pod
  • jenkins调用Kubernetes API创建Pod,作为Jenkins slave节点加入到jenkins
  • 直接指定nodeName强制调度到Virtual-kubelet节点,指定SPOT实例的规格,价格上限
  • 构建过程使用kaniko构建容器镜像,推送到自建的harbor镜像源服务器

准备阶段

创建Secret

  • 命名空间根据自己的情况灵活调整
1
2
3
4
5
6
kubectl -n jenkins create secret docker-registry \
harbor-registry-secret \
--docker-server=DOCKER_REGISTRY_SERVER \
--docker-username=DOCKER_USER \
--docker-password=DOCKER_PASSWORD \
--docker-email=DOCKER_EMAIL

配置jenkinsfile

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/*
1. 指定nodeName调度到ECI
2. 指定annotations使用spot实例,配置最高出价
3. 挂载dockerconfigjson到kaniko实现免密
*/
def PodTemplate = '''
apiVersion: "v1"
kind: "Pod"
metadata:
annotations:
k8s.aliyun.com/eci-use-specs : "ecs.c6.xlarge"
k8s.aliyun.com/eci-spot-strategy: "SpotWithPriceLimit"
k8s.aliyun.com/eci-spot-price-limit: "0.250"
labels:
jenkins: "slave"
jenkins/jnlp-agent: "true"
spec:
containers:
- env:
- name: "TZ"
value: "Asia/Shanghai"
- name: "JENKINS_AGENT_WORKDIR"
value: "/home/jenkins/agent"
- name: "JENKINS_URL"
value: "http://jenkins-service/"
image: "jenkins/jnlp-slave:4.3-9"
imagePullPolicy: "IfNotPresent"
name: "jnlp"
tty: true
volumeMounts:
- name: "jenkins-nfs"
mountPath: "/jenkins_outputs"
readOnly: false
- name: timezone
mountPath: /etc/localtime
subPath: Shanghai
- mountPath: /root/.docker
name: dockerconfigjson
readOnly: true
workingDir: "/home/jenkins/agent"
- command:
- /busybox/cat
env:
- name: TZ
value: Asia/Shanghai
- name: LANG
value: C.UTF-8
image: gcr.io/kaniko-project/executor:debug-v1.0.0
imagePullPolicy: IfNotPresent
name: kaniko
tty: true
volumeMounts:
- mountPath: /kaniko/.docker
name: dockerconfigjson
readOnly: true
workingDir: /home/jenkins/agent
hostNetwork: false
nodeName: virtual-node-eci-0
restartPolicy: "Never"
serviceAccount: "jenkins"
volumes:
- name: dockerconfigjson
secret:
secretName: harbor-registry-secret
'''
pipeline {
agent{
kubernetes {
yaml PodTemplate
defaultContainer "jnlp"
}
}
stages {
stage ("拉取代码") {
steps {
script {
echo "拉代码"
}
script {
/* 这里简单写一个FROM nginx:1.18 */
writeFile encoding: 'UTF8', file: './Dockerfile', text: 'FROM nginx:1.18'
}
}
}
stage ("构建docker镜像") {
steps {
/* 切到kaniko构建docker镜像 */
/* 构建过程使用阿里云容器镜像代理 */
/* 同时推送到多个不同的镜像仓库 */
container(name: 'kaniko', shell: '/busybox/sh') {
script {
sh '''
#!/busybox/sh
/kaniko/executor \
--context \$(pwd) \
--verbosity "info" \
--registry-mirror "pqbap4ya.mirror.aliyuncs.com" \
--destination "dockerregistry/nginx:1.18" \
--destination "dockerregistry/nginx:1.18-debian" \
--destination "dockerregistry/nginx:latest"
'''
}
}
}
}
}
}