在 AWS 上使用 Kubernetes v1.16,我在尝试减少在新生成的节点上启动 pod 所需的时间时遇到了一个奇怪的问题。
默认情况下,节点 AMI 不包含任何预缓存的 docker 镜像,因此当 pod 被调度到其上时,它的第一个工作是拉取 docker 镜像。
拉取大型 docker 镜像可能需要一段时间,因此 pod 需要很长时间才能运行。
最近我想到了将大型 docker 镜像预先拉入 AMI 的想法,这样当 pod 被安排到它上面时,就不必下载它。事实证明,很多人都在做这个 dor 一段时间,被称为“烘焙 AMI”:
我的问题是,当我生成一个带有大映像的 AMI 并使用该 AMI 时,一切都按预期运行,并且 docker 映像不会像已经存在的那样下载,因此 pod 几乎在 1 秒内启动,但 pod 本身运行了 1000 次比未在 AMI 上预先拉取 docker 映像的速度慢。
我在做什么:
如果我不预拉我的 docker 镜像,那么只有当我使用生成的新 AMI 预拉它时,它才能正常运行,然后如果它在一秒钟内运行,那么容器将变得前所未有的缓慢。
我的docker镜像使用GPU资源,它基于tensorflow/tensorflow:1.14.0-gpu-py3镜像。这似乎与使用nvidia-docker和tensorflow om GPU启用的EC2有关。
如果有人知道这种极端的运行延迟从何而来,我们将不胜感激。
编辑#1
从那时起,我现在使用 Packer 来构建我的 AMI。 这是我的模板文件:
{
"builders": [
{
"type": "amazon-ebs",
"access_key": "{{user `aws_access_key`}}",
"secret_key": "{{user `aws_secret_key`}}",
"ami_name": "compute-{{user `environment_name`}}-{{timestamp}}",
"region": "{{user `region`}}",
"instance_type": "{{user `instance`}}",
"ssh_username": "admin",
"source_ami_filter": {
"filters": {
"virtualization-type": "hvm",
"name": "debian-stretch-hvm-x86_64-gp2-*",
"root-device-type": "ebs"
},
"owners":"379101102735",
"most_recent": true
}
}
],
"provisioners": [
{
"execute_command": "sudo env {{ .Vars }} {{ .Path }}",
"scripts": [
"ami/setup_vm.sh"
],
"type": "shell",
"environment_vars": [
"ENVIRONMENT_NAME={{user `environment_name`}}",
"AWS_ACCOUNT_ID={{user `aws_account_id`}}",
"AWS_REGION={{user `region`}}",
"AWS_ACCESS_KEY_ID={{user `aws_access_key`}}",
"AWS_SECRET_ACCESS_KEY={{user `aws_secret_key`}}",
"DOCKER_IMAGE_NAME={{user `docker_image_name`}}"
]
}
],
"post-processors": [
{
"type": "manifest",
"output": "ami/manifest.json",
"strip_path": true
}
],
"variables": {
"aws_access_key": "{{env `AWS_ACCESS_KEY_ID`}}",
"aws_secret_key": "{{env `AWS_SECRET_ACCESS_KEY`}}",
"environment_name": "",
"region": "eu-west-1",
"instance": "g4dn.xlarge",
"aws_account_id":"",
"docker_image_name":""
}
}
这是为 Docker 和 Nvidia Docker 配置 AMI 的相关脚本:
#!/bin/bash
cd /tmp
export DEBIAN_FRONTEND=noninteractive
export APT_LISTCHANGES_FRONTEND=noninteractive
# docker
apt-get update
apt-get install -y apt-transport-https ca-certificates curl gnupg2 software-properties-common
curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian stretch stable"
apt-get update
apt-get install -y docker-ce
usermod -a -G docker $USER
# graphical drivers
apt-get install -y software-properties-common
wget http://us.download.nvidia.com/XFree86/Linux-x86_64/440.64/NVIDIA-Linux-x86_64-440.64.run
bash NVIDIA-Linux-x86_64-440.64.run -sZ
# nvidia-docker
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | tee /etc/apt/sources.list.d/nvidia-docker.list
apt-get update
apt-get install -y nvidia-container-toolkit
apt-get install -y nvidia-docker2
cat > /etc/docker/daemon.json <<EOL
{
"default-runtime": "nvidia",
"runtimes": {
"nvidia": {
"path": "/usr/bin/nvidia-container-runtime",
"runtimeArgs": []
}
}
}
EOL
systemctl restart docker
# enable nvidia-persistenced service
cat > /etc/systemd/system/nvidia-persistenced.service <<EOL
[Unit]
Description=NVIDIA Persistence Daemon
Wants=syslog.target
[Service]
Type=forking
PIDFile=/var/run/nvidia-persistenced/nvidia-persistenced.pid
Restart=always
ExecStart=/usr/bin/nvidia-persistenced --verbose
ExecStopPost=/bin/rm -rf /var/run/nvidia-persistenced
[Install]
WantedBy=multi-user.target
EOL
systemctl enable nvidia-persistenced
# prepull docker
apt-get install -y python3-pip
pip3 install awscli --upgrade
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
docker pull $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$DOCKER_IMAGE_NAME:$ENVIRONMENT_NAME
# Clean up
apt-get -y autoremove
apt-get -y clean
无论如何,一旦我写下这句话:
docker pull $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$DOCKER_IMAGE_NAME:$ENVIRONMENT_NAME
我面临着同样奇怪的问题,当 Pod 被调度到从此 AMI 启动的节点上时,它会显示“图像已存在于机器上”,因此它不会再次拉取它,但是使用 TensorFlow 时容器会非常慢,例如。 ts.Session() 需要大约一分钟的时间来运行。
编辑#2
添加有关 pod 上执行内容的额外信息:
Dockerfile
FROM tensorflow/tensorflow:1.14.0-gpu-py3
CMD ["python", "main.py"]
主.py
import tensorflow as tf
config = tf.ConfigProto(allow_soft_placement=True)
config.gpu_options.allow_growth = True
return tf.Session(graph=tf.Graph(), config=config)
仅使用这些行,当图像被预拉时,TF 会话初始化最多需要 1 分钟才能完成,而当图像未预拉时,则需要 1 秒。
这很可能是由于新实例没有“完全下载”磁盘的所有部分。 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-initialize.html 有这方面的详细信息。
对于从快照创建的卷,必须先从 Amazon S3 拉取存储块并将其写入卷,然后才能访问它们。此初步操作需要时间,并且可能会导致首次访问每个块时 I/O 操作的延迟显着增加。所有块下载并写入卷后,即可实现卷性能。
当图像从S3完全下载后,我假设一切都会恢复正常速度。