使用镜像流及附加磁盘为拉取镜像加速
GKE的用户越来越多,近期很多大模型也开始部署在GKE。但是随着镜像越来越大,创建pod时,镜像的拉取速度成为瓶颈。
去年,GKE推出了镜像流加速(ImageStreaming),结合Artifact Registry实现node pool层的镜像缓存加速,但是这种方式有一定限制,镜像必须保存在Artifact Registry,并且第一次拉取镜像时速度仍然较慢。前不久,GCP又推出了通过附加磁盘加速镜像的方式,解决了这两个限制。
GCP官方文档相关说明和操作步骤,但是写的不够详细,并且有些小错误和小坑,下边做详细解释,并比较各种方式镜像拉取速度。
创建带Docker Image的附加磁盘镜像
使用此功能前,需要先创建带有所需Docker Image的磁盘镜像,然后在创建nood pool时,指定在node挂接通过此镜像生成的附加磁盘。
环境准备
所需代码
在Github上可以下载创建磁盘镜像所需的代码:
gke-disk-image-builder
该代码为go语言编写,所以先要预备go的执行环境。
此代码的功能是创建一台临时GCP VM,并附加一块磁盘,将指定的Docker Image下载到该磁盘后,将该磁盘做成磁盘镜像。
执行权限
代码执行时,需要一定GCP权限:
- compute.projects.get
- storag.objects.create
- serviceusage.services.use
- iam.serviceAccountUser
如果在具备gcloud环境,可以通过ADC赋予这些权限;如果在其他环境,可以创建Service Account并下载key,在执行创建磁盘镜像命令时通过参数指定key。
GCS Bucket
需创建一个GCS Bucket,存放创建磁盘镜像过程中的日志。代码执行过程中,会创建一个临时GCP VM,该VM使用默认的GCE Default Service Account,所以需要给GCE Default Service Account绑定权限。在文档中,要求赋予storage.objectCreator的role,但实际运行时,需要有storage.objects.get权限,所以应该赋予storage.objectAdmin权限。
如下图
为GCE Default Service Account赋予权限:
创建磁盘镜像
使用默认参数创建磁盘镜像
创建磁盘镜像最简单的方式是在CloudShell里执行。CloudShell已具备go执行环境,并且具备登录人员的对应权限,所以只需下载代码进入相关目录即可。
从Github下载代码并进入相关目录:
1 | git clone https://github.com/GoogleCloudPlatform/ai-on-gke.git |
在CloudShell执行如下:
创建磁盘镜像的必须参数如下:
1 | go run ./cli \ |
其中
- project-name:项目的id(注意参数名是name,但实际需要id,是个小坑)
- image-name:创建的磁盘镜像名字,不能和已有的重复,否则会报错
- zone:临时GCP VM所在zone,随意即可
- gcs-path:存放日志的GCS Bucker名字
- disk-size-gb:镜像容量,GB为单位,整数,不需后缀
- container-image:所需拉取的Docker Image名字(地址),可以放多个,每个Image均需要用此参数指定
如下成功创建:
创建磁盘镜像过程中的日志保存在预先创建的GCS Bucket,每次执行都会创建一个新目录:
进入该目录,可以查看日志:
其他参数
根据实际情况,该代码还支持更多参数,常用如下:
- –gcp-oauth:如之前所述,如果没哟欧ADC环境或ADC环境权限不足,需要使用ServiceAccount的key认证,可以通过此参数设置key的路径,注意要绝对路径
- –service-account:创建临时GCP VM时,为该VM配置的Service Account
- –image-pull-auth:私有库的认证,下一节详细描述
- –timeout:每个步骤的最大时长,默认20分钟,如果网络较慢,或者Docker Image较大,可以适当增加
- –network&–subnet:创建GCP VM的vpc和subnet,默认值Default,可根据实际情况配置
从私有镜像库拉取Docker Image
上个例子中,拉取的Docker Image(us-docker.pkg.dev/hy-tool/hy-docker-repo/hy-image-001:1.0)存放在公有镜像库us-docker.pkg.dev/hy-tool/hy-docker-repo/,所以无需任何认证。
现在用一个私有库的Doceker Image(us-docker.pkg.dev/hy-tool/hy-docker-repo-private/hy-image-001:1.0)测试:
由于权限问题,在拉取Docker Image时出错。这时候我们需要通过参数–image-pull-auth解决。
–gcp-oauth有两个选项,默认是None,即没有任何认证。另一个选项是ServiceAccountToken,当设置为ServiceAccountToken时时,将使用临时VM的ServiceAccount作为鉴权去拉取Docker Image。
现在使用Default GCE Service Account测试,将其赋予私有镜像库的viewer权限:
然后在创建镜像的命令里加上参数–image-pull-auth=ServiceAccountToken后执行:
这次拉取成功并创建了镜像。
如前边所述,同一个磁盘镜像中可以拉取多个Docker Image,并且多个Docker Image可以存放在不同的镜像库。但因为参数–image-pull-auth是全局的,所以多个Docker Image必须全在公有镜像库或者全在私有镜像库。
Docker Image拉取速度比较
现在GKE上拉取Docker Image有三种方式:
- 直接从镜像库拉取
- 采用镜像流传输加速
- 通过本地磁盘加速
现在对这三种方式进行比较。
ImageStreaming是基于Region级别,所以为了避免相互干扰,将创建3个不同Region的Cluster,每个Cluster创建NodePool进行测试。每个NodePool配置2个Node,机型为e2-standard-4(16GB内存)。
测试环境准备
普通集群
在us-west1-a创建普通Cluster,及普通NodePool:
1 | gcloud container clusters create cluster001 --zone=us-west1-a |
启用ImageStreaming的集群
在us-west2-a创建启用ImageStreaming的Cluster,及启用ImageStreaming的NodePool
1 | gcloud container clusters create cluster001 --zone=us-west2-a --enable-image-streaming |
启用附加磁盘的集群
在us-west3-a创建启用附加磁盘的Cluster,及启用附加磁盘的NodePool(使用附加磁盘功能需启用ImageStreaming):
1 | gcloud container clusters create cluster001 --zone=us-west3-a --enable-image-streaming |
查看Node配置,会发现多一块从磁盘镜像复制的附加磁盘:
拉取速度比较
创建yaml文件如下:
1 | apiVersion: apps/v1 |
这里使用了一个2GB的Docker Image,并且要求内存为10G,接近单个Node的内存上限,这样每个Node只能运行一个Pod。
普通NodePool拉取
在普通NodePool创建pod,并通过kubectl get events查看Docker Image拉取时间:
拉取时间大约55s。
现将pod副本适量设置为2,由于内存要求,将部署到新的Node:
新的Node需要再次从镜像库拉取,耗时43s。
启用ImageStreaming的NodePool拉取
同样查看第一个Node的拉取时间:
大约53s。
在新Node拉取:
可以看到,镜像通过ImageStreaming方式从本地缓存拉取,立刻完成。
启用附加磁盘的NodePool拉取
查看Docker Image拉取时间:
可以看到,即使第一次拉取,Docker Image从本地磁盘提供,只花了不到1s的时间。
总结
通过以上实验可以看到,ImageStreaming和附加磁盘的方式可以显著提升Docker Image的拉取速度,特别是现在比较火热的AI模型,都会采用比较大的Docker Image,可以用这些方式加速。
ImageStreaming和附加磁盘的实现方式不一样,使用场景也有区别,简单总结如下:
ImageStreaming | 附加磁盘 | |
---|---|---|
第一次拉取 | 无加速 | 有加速 |
新Node拉取 | 同Region有加速 | 有加速 |
使用限制 | 只能对Artifact Registry的Docker Image加速 | 需要预先创建包含Docker Image的磁盘镜像,操作较为繁琐 |
大家可以根据实际情况选择最适合自己的方式。