HPC 高性能计算(High Performance Computing),是指利用大规模并行化的计算资源来解决大规模、高复杂度的计算问题。HPC 通常用于科学计算、工程计算、大数据分析、量化金融等领域,包括气象预报、地震模拟、基因组分析、人工智能等。传统的 HPC 系统复杂,需要专业人员进行维护和管理;缺乏灵活性,不易适应不同的应用场景;不利于快速迭代和开发,部署和更新成本较高。相比之下,容器化 HPC 具有更好的资源管理和调度、应用程序隔离和安全性、计算资源的灵活性和可伸缩性、应用程序部署和更新的简便和快速性、以及更加灵活和可配置的运行环境等优势。帮助用户更加高效地利用计算资源,提高计算效率和性能,从而推动科学计算和数据分析等领域的发展和应用。
STOmics
时空组学(STOmics)技术 Stereo-seq 基于 DNA 纳米球测序技术研发而成,是一项同时具备高精度和大视场的空间转录组技术。该技术将认识生命的分辨率推进到了 500nm 的亚细胞尺度,相比过去同类技术,分辨率提高了 200 倍,被认为是生命科学领域的重大突破。该技术不仅能够实现亚细胞级分辨和厘米级全景视场,同时还可以实现基因和影像的同时分析。通过在生命时空图谱、物种演化、疾病诊断和治疗等领域规模化应用,未来将全面提升人类对生命的认知并推动疾病重新定义。
时空组学需要处理多组学数据,包括基因组、转录组、蛋白质组和代谢组等。这些不同类型的数据需要整合和分析,以探索它们之间的相互关系以及如何随时间和空间的变化而变化。因此进行个性化分析是十分必要的,个性化分析可以帮助科学家快速探索生命科学中的新现象和新机制,推动生命科学的发展和应用。随着时空组学技术的发展,海量时空组数据将不断涌现。如何高效智能地对时空数据进行分析,是当前亟需解决的问题。使用容器化 HPC 平台可以更加高效的利用计算资源,提高计算效率和性能;同时也支持异构计算的方式和 Spot,进一步降低成本;容器化使系统变得更加灵活,支持在线和离线任务等多种业务模式,同时也带来了跨不同环境的应用程序的可移植性,可以更好的实现混合云架构。
架构图概览
本文涉及主要软件和服务介绍
Amazon EKS
Amazon EKS 是一项托管 Kubernetes 服务,用于在 AWS Cloud 和本地数据中心上运行 Kubernetes。在云中,Amazon EKS 可自动管理负责安排容器、管理应用程序可用性、存储集群数据和其他关键任务的 Kubernetes 控制面板节点的可用性和可扩展性。通过 Amazon EKS,您可以利用 AWS 基础设施的所有性能、规模、可靠性和可用性,以及与 AWS 网络和安全服务的集成。EKS 在本地提供一个一致且完全支持的 Kubernetes 解决方案,其中集成了工具和 AWS Outposts、虚拟机或裸机服务器的简单部署。
Karpenter
Karpenter 是一款使用 AWS 构建的、开源的、灵活的 Kubernetes 集群自动扩缩程序。它通过快速启动适当规模的计算资源来响应不断变化的应用程序负载,从而提高应用程序可用性和集群效率。Karpenter 还提供实时计算资源以满足应用程序需求,并且可以快速自动优化集群的计算资源占用空间,以降低成本并提高性能。Karpenter 是根据 Apache 许可证 2.0 授权的开源项目,旨在与任何运行 Kubernetes 集群的环境一起使用,包括所有主要的云提供商和本地部署环境。
Volcano
Volcano 是基于 Kubernetes 的容器批量计算平台,主要用于高性能计算场景。它提供了 Kubernetes 目前缺少的一套机制,这些机制通常是机器学习大数据应用、科学计算、特效渲染等多种高性能工作负载所需的。作为一个通用批处理平台,Volcano 与几乎所有的主流计算框架无缝对接,如 Spark 、TensorFlow 、PyTorch 、 Flink 、Argo 、MindSpore 、 PaddlePaddle 等。它还提供了包括基于各种主流架构的 CPU、GPU 在内的异构设备混合调度能力。Volcano 的设计理念建立在 15 年来多种系统和平台大规模运行各种高性能工作负载的使用经验之上,并结合来自开源社区的最佳思想和实践。
Jupyter Notebook
Jupyter Notebook 是一种交互式计算环境,可以让用户在单个文档中编写、运行代码,并可将其与富文本(如 Markdown 和 LaTeX)和多媒体内容(如图像和视频)结合起来展示。它支持多种编程语言,包括 Python、R 和 Julia 等,用户可以使用浏览器访问 Notebook,或将其导出为多种格式的文档。Jupyter Notebook 可以用于数据分析、科学计算、机器学习等多种场景。
先决条件
- 创建 AWS 资源的权限(例如 IAM 角色、IAM 策略、Amazon EC2 实例和 Amazon EKS 集群)
- 创建一台用于操作的 EC2 并赋予角色,该角色具有 admin 权限
- 熟悉 EC2 服务的使用,熟悉 EFS、S3 等存储服务的使用
- 熟悉 Kubernetes 和 Linux shell 命令的基本使用
部署步骤
该方案包含以下步骤,用于创建和配置所有必要的资源,其中使用 Karpenter 实现集群的自动扩缩,批量计算调度使用 Volcano,AWS Load Balancer Controller 作为应用负载均衡器,对外暴露 Notebook 的访问链接。
- Step 1 – Install Kubernetes Tools
- Step 2 – Configure setting
- Step 3 – Create EKS Cluster with eksctl
- Step 5 – Create Karpenter
- Step 6 – Create AWS Load Balancer Controller
- Step 7 – Create Notebook Image to ECR
- Step 8 – Test the Notebook
Step 1 – Install Kubernetes Tools
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
export PATH=/usr/local/bin:$PATH
source ~/.bash_profile
aws --version
curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv -v /tmp/eksctl /usr/local/bin
eksctl version
sudo curl -o /usr/local/bin/kubectl https://s3.us-west-2.amazonaws.com/amazon-eks/1.25.6/2023-01-30/bin/linux/amd64/kubectl
sudo chmod +x /usr/local/bin/kubectl
kubectl version --client=true --short=true
curl -sSL https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
helm repo add stable https://charts.helm.sh/stable
helm completion bash >> ~/.bash_completion
. /etc/profile.d/bash_completion.sh
. ~/.bash_completion
source <(helm completion bash)
helm version --short
sudo yum -y install jq gettext bash-completion moreutils git
sudo amazon-linux-extras install docker -y
sudo service docker start
sudo usermod -a -G docker ec2-user
sudo chkconfig docker on
安装完 Docker 后,退出当前登录的 Session,再重新登录。
Step 2 – Configure setting
修改 CLUSTER_NAME
为自己的集群名字,BUCKET_NAME
为被 pod 访问的 Bucket。
export CLUSTER_NAME="STOmics-notebook"
export BUCKET_NAME="pcluster-2023-02-09-d2387416"
export KARPENTER_VERSION=v0.27.2
export TEMPOUT=$(mktemp)
export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
export AWS_REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region')
export AZS=($(aws ec2 describe-availability-zones --query 'AvailabilityZones[].ZoneName' --output text --region $AWS_REGION))
echo "export ACCOUNT_ID=${ACCOUNT_ID}" | tee -a ~/.bash_profile
echo "export AWS_REGION=${AWS_REGION}" | tee -a ~/.bash_profile
echo "export CLUSTER_NAME=${CLUSTER_NAME}" | tee -a ~/.bash_profile
echo "export BUCKET_NAME=${BUCKET_NAME}" | tee -a ~/.bash_profile
echo "export AZS=(${AZS[@]})" | tee -a ~/.bash_profile
echo "export KARPENTER_VERSION=${KARPENTER_VERSION}" | tee -a ~/.bash_profile
echo "export TEMPOUT=${TEMPOUT}" | tee -a ~/.bash_profile
echo 'export LBC_VERSION="v2.4.7"' >> ~/.bash_profile
echo 'export LBC_CHART_VERSION="1.4.1"' >> ~/.bash_profile
. ~/.bash_profile
aws configure set default.region ${AWS_REGION}
aws configure get default.region
Step 3 – Create EKS Cluster with eksctl
mkdir -p ~/eks-workloads && cd ~/eks-workloads
- 使用 CloudFormation 初始化 Karpenter 组件信息
curl -fsSL https://karpenter.sh/"${KARPENTER_VERSION}"/getting-started/getting-started-with-karpenter/cloudformation.yaml > $TEMPOUT \
&& aws cloudformation deploy \
--stack-name "Karpenter-${CLUSTER_NAME}" \
--template-file "${TEMPOUT}" \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides "ClusterName=${CLUSTER_NAME}"
- 创建集群(1.25)配置文件(eks-cluster.yaml)
cat << EOF > eks-cluster.yaml
---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: ${CLUSTER_NAME} # EKS Cluster name
region: ${AWS_REGION} # Region Code to place EKS Cluster
version: "1.25"
tags:
karpenter.sh/discovery: ${CLUSTER_NAME}
vpc:
cidr: "11.0.0.0/16" # CIDR of VPC for use in EKS Cluster
nat:
gateway: HighlyAvailable
iam:
withOIDC: true
serviceAccounts:
- metadata:
name: karpenter
namespace: karpenter
roleName: ${CLUSTER_NAME}-karpenter
attachPolicyARNs:
- arn:aws:iam::${ACCOUNT_ID}:policy/KarpenterControllerPolicy-${CLUSTER_NAME}
roleOnly: true
iamIdentityMappings:
- arn: "arn:aws:iam::${ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}"
username: system:node:{{EC2PrivateDNSName}}
groups:
- system:bootstrappers
- system:nodes
managedNodeGroups:
- name: ${CLUSTER_NAME}-ng # Name of node group in EKS Cluster
instanceType: c6i.large # Instance type for node group
desiredCapacity: 2 # The number of worker node in EKS Cluster
minSize: 1
maxSize: 10
volumeSize: 100 # EBS Volume for worker node (unit: GiB)
privateNetworking: true
ssh:
enableSsm: true
iam:
withAddonPolicies:
imageBuilder: true # Add permission for Amazon ECR
albIngress: true # Add permission for ALB Ingress
cloudWatch: true # Add permission for CloudWatch
autoScaler: true # Add permission Auto Scaling
ebs: true # Add permission EBS CSI driver
cloudWatch:
clusterLogging:
enableTypes: ["*"]
EOF
eksctl create cluster -f eks-cluster.yaml
集群完全部署大约需要 15 到 20 分钟。您可以在终端中查看集群部署的进度,也可以在 AWS CloudFormation 控制台中查看事件和资源的状态。
Step 4 – Create Volcano
要求:Kubernetes 集群,集群版本不低于 V1.13,支持 CRD。
kubectl apply -f https://raw.githubusercontent.com/volcano-sh/volcano/master/installer/volcano-development.yaml
kubectl get all -n volcano-system
输出结果:
NAME READY STATUS RESTARTS AGE
pod/volcano-admission-76cfcc58f6-fnh5w 1/1 Running 0 90s
pod/volcano-admission-init-m6fxl 0/1 Completed 0 90s
pod/volcano-controllers-6869c78b5b-qhd77 1/1 Running 0 89s
pod/volcano-scheduler-5c7d8679b4-5mstl 1/1 Running 0 88s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/volcano-admission-service ClusterIP 172.20.37.119 <none> 443/TCP 90s
service/volcano-scheduler-service ClusterIP 172.20.62.221 <none> 8080/TCP 88s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/volcano-admission 1/1 1 1 90s
deployment.apps/volcano-controllers 1/1 1 1 89s
deployment.apps/volcano-scheduler 1/1 1 1 88s
NAME DESIRED CURRENT READY AGE
replicaset.apps/volcano-admission-76cfcc58f6 1 1 1 90s
replicaset.apps/volcano-controllers-6869c78b5b 1 1 1 89s
replicaset.apps/volcano-scheduler-5c7d8679b4 1 1 1 88s
NAME COMPLETIONS DURATION AGE
job.batch/volcano-admission-init 1/1 9s 90s
Step 5 – Create Karpenter
export CLUSTER_ENDPOINT="$(aws eks describe-cluster --name ${CLUSTER_NAME} --query "cluster.endpoint" --output text)"
export KARPENTER_IAM_ROLE_ARN="arn:aws:iam::${ACCOUNT_ID}:role/${CLUSTER_NAME}-karpenter"
echo $CLUSTER_ENDPOINT $KARPENTER_IAM_ROLE_ARN
aws iam create-service-linked-role --aws-service-name spot.amazonaws.com || true
# If the role has already been successfully created, you will see:
# An error occurred (InvalidInput) when calling the CreateServiceLinkedRole operation: Service role name AWSServiceRoleForEC2Spot has been taken in this account, please try a different suffix.
docker logout public.ecr.aws
helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter --version ${KARPENTER_VERSION} --namespace karpenter --create-namespace \
--set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=${KARPENTER_IAM_ROLE_ARN} \
--set settings.aws.clusterName=${CLUSTER_NAME} \
--set settings.aws.defaultInstanceProfile=KarpenterNodeInstanceProfile-${CLUSTER_NAME} \
--set settings.aws.interruptionQueueName=${CLUSTER_NAME} \
--wait
cat <<EOF | kubectl apply -f -
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
name: default
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
limits:
resources:
cpu: 1000
providerRef:
name: default
ttlSecondsAfterEmpty: 30
---
apiVersion: karpenter.k8s.aws/v1alpha1
kind: AWSNodeTemplate
metadata:
name: default
spec:
subnetSelector:
karpenter.sh/discovery: ${CLUSTER_NAME}
securityGroupSelector:
karpenter.sh/discovery: ${CLUSTER_NAME}
EOF
- 创建 deployment 用于测试 Karpenter 的弹性伸缩,将 replicas 改为 5
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: inflate
spec:
replicas: 0
selector:
matchLabels:
app: inflate
template:
metadata:
labels:
app: inflate
spec:
terminationGracePeriodSeconds: 0
containers:
- name: inflate
image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
resources:
requests:
cpu: 1
EOF
kubectl scale deployment inflate --replicas 5
kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller
kubectl delete deployment inflate
kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller
kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller
Step 6 – Create AWS Load Balancer Controller
AWS Load Balancer Controller 是一个控制器,用于帮助管理 Kubernetes 集群的弹性负载均衡器。
- 它通过配置 Application Load Balancers 来满足 Kubernetes Ingress 资源的需求。
- 它通过配置 Network Load Balancers 来满足 Kubernetes Service 资源的需求。
该步骤创建 AWS Load Balancer Controller 将使用的 IAM 策略。该 IAM 策略将关联到 Kubernetes Service Account,并允许 controller pods 在您的 AWS 账户中创建和管理 ELB 资源。
curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/${LBC_VERSION}/docs/install/iam_policy.json
aws iam create-policy --policy-name AWSLoadBalancerControllerIAMPolicy --policy-document file://iam_policy.json
rm iam_policy.json
- Create a IAM role and ServiceAccount for the Load Balancer controller
该步骤创建名为 aws-load-balancer-controller
的 Kubernetes Service Account。
eksctl create iamserviceaccount --cluster ${CLUSTER_NAME} --namespace kube-system --name aws-load-balancer-controller --attach-policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/AWSLoadBalancerControllerIAMPolicy --override-existing-serviceaccounts --approve
上面的命令部署了一个 CloudFormation 模板,该模板创建了一个 IAM 角色并将 IAM 策略附加到它。IAM 角色与 Kubernetes Service Account 相关联。您可以使用以下命令查看已经创建的 Service Account 的详细信息。
kubectl get sa aws-load-balancer-controller -n kube-system -o yaml
- Install the TargetGroupBinding CRDs
kubectl apply -k "github.com/aws/eks-charts/stable/aws-load-balancer-controller/crds?ref=master"
- Deploy the Helm chart from the Amazon EKS charts repo
helm repo add eks https://aws.github.io/eks-charts
export VPC_ID=$(aws eks describe-cluster --name "${CLUSTER_NAME}" --query "cluster.resourcesVpcConfig.vpcId" --output text)
helm upgrade -i aws-load-balancer-controller \eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=${CLUSTER_NAME} \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller \
--set image.tag="${LBC_VERSION}" \
--set region=${AWS_REGION} \
--set vpcId=${VPC_ID} \
--version="${LBC_CHART_VERSION}"
可以通过以下的命令查看部署是否成功:
kubectl -n kube-system rollout status deployment aws-load-balancer-controller
Step 7 – Create Notebook Image to ECR
该步骤创建的是一个示例 Notebook,可以根据自己的业务逻辑封装自己的 Notebook images。
docker pull jupyter/datascience-notebook
export NOTE_BOOK_REPOSITORY_NAME=datascience-notebook
echo "export REPOSITORY_NAME=${NOTE_BOOK_REPOSITORY_NAME}" | tee -a ~/.bash_profile
aws ecr create-repository --repository-name ${NOTE_BOOK_REPOSITORY_NAME}
export REPO_URI=$(aws ecr describe-repositories --repository-names=${NOTE_BOOK_REPOSITORY_NAME} |jq -r '.repositories[0].repositoryUri')
echo "export REPO_URI=${REPO_URI}" | tee -a ~/.bash_profile
echo $REPO_URI
aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com
docker tag jupyter/datascience-notebook:latest ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/datascience-notebook:latest
docker push ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/datascience-notebook:latest
Step 8 – Test the Notebook
创建 Notebook 部署文件:
cat > notebook.yaml << EOF
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: default
name: deployment-notebook
spec:
selector:
matchLabels:
app.kubernetes.io/name: app-notebook
replicas: 1
template:
metadata:
labels:
app.kubernetes.io/name: app-notebook
spec:
containers:
- image: ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/datascience-notebook:latest
imagePullPolicy: Always
name: app-notebook
ports:
- containerPort: 8888
---
apiVersion: v1
kind: Service
metadata:
namespace: default
name: service-notebook
spec:
ports:
- port: 80
targetPort: 8888
protocol: TCP
type: NodePort
selector:
app.kubernetes.io/name: app-notebook
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: default
name: ingress-notebook
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-notebook
port:
number: 80
EOF
以验证 AWS Load Balancer Controller 是否根据 Ingress 对象创建 Application Load Balancer。
kubectl apply -f notebook.yaml
kubectl -n default rollout status deployment deployment-notebook
echo http://$(kubectl get ingress/ingress-notebook -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')
ALB 设置需要一点时间,大概等待 3-5 分钟后,在浏览器中输入打印的 URL,网页正常显示。
以上是在线 Notebook 的简单示例,可以在 Notebook 的镜像里加入自己的业务逻辑或者与业务系统进行集成。
容器化 HPC 与 STOmics 云平台集成
在线任务场景
创建在线分析 Task
等待大概 1 分钟,就可以打开 Notebook 进行在线任务分析研究
查看 EKS 集群上对应的 Pod
离线任务场景
创建离线 Task
创建完后台自动运行该任务,在界面中可以查看任务的状态等相关信息
查看 EKS 集群上对应的 Pod
总结
本文介绍了如何使用 Amazon EKS 服务构建容器化 HPC 平台,用于 STOmics 的个性化分析工作场景。该方案中 Kubernetes 采用 1.25 的版本,基于该版本分别安装 Karpenter、Volcano、AWS Load Balancer Controller 等插件,最后通过创建一个 Notebook 的示例来验证部署结果。文章详细介绍了每个步骤需要执行的命令和操作,并提供了相应的命令行代码示例,适合有一定 AWS 和 Kubernetes 经验的读者阅读。借助容器化的 HPC 平台方案,科学家可以在 STOmics 研究上进行快速创新和试错,同时借助云上大规模的计算能力,解决了当前时空组学大数据面临的算力紧缺问题,并且显著的降低了成本,实现了更好的时效性,这将大大提高生命科学领域的多组学数据分析效率,加速科研进程,助力科研转化。后续我们也会深入的研究存储在容器化 HPC 平台方面的最佳实践以及 STOmics 标准分析、高级分析方面的优化工作。
参考链接
Amazon EKS:https://thinkwithwp.com/cn/eks/
Karpenter:https://karpenter.sh/
Volcano:https://volcano.sh/zh/docs/
STOmics Cloud:https://www.bilibili.com/read/cv22107301