在创建启动模板和计算节点组之前,我们需要了解 PCS 如何使用 Amazon Machine Images (AMIs)。
PCS 使用我们提供的 AMI。与EKS不同,PCS 不维护自己的 AMI 。我们负责构建和维护计算节点使用的 AMI。
每个与 PCS 兼容的 AMI 必须安装两样东西:
aws-pcs-repo S3 存储桶安装。除了这两个要求之外,我们还可以安装所需的任何软件 — 驱动程序、库、监控代理、身份验证客户端。
AWS 提供示例 AMI 供快速测试:
aws ec2 describe-images \
--owners amazon \
--filters "Name=name,Values=aws-pcs-sample_ami-*-x86_64-slurm-*" \
--query "Images | sort_by(@, &CreationDate) | [-1].{Name:Name,ImageId:ImageId}" \
--output table \
--region us-east-2
示例 AMI 仅供演示使用,不建议用于生产工作负载。它们可能不包含我们特定实例类型所需的驱动程序或配置。

对于生产环境(以及本次研讨会),我们应该使用包含工作负载所需的特定驱动程序和软件的自定义 AMI。
cloudformation创建的 AMI 是使用 EC2 Image Builder 从 Amazon Linux 2023 基础镜像构建的。它包括:
| 组件 | 用途 |
|---|---|
| PCS agent | 向 PCS 注册节点 |
| Slurm | 作业调度(与集群版本匹配) |
| EFA 驱动程序 | 低延迟节点间网络(EFA 安装程序还捆绑了 OpenMPI 5 和 environment-modules 系统) |
| Lustre 客户端 | 挂载 FSx for Lustre 文件系统 |
| EFS utils | 挂载 EFS 文件系统 |
| CloudWatch agent | 实例级指标和日志收集 |
| SSSD + LDAP 客户端 | 针对 LDAP 服务器进行用户身份验证(预先配置) |
| Development Tools | gcc、g++、make 用于编译 MPI 程序 |
| Utilities | htop、vim、jq |
SSSD 配置已内置到 AMI 中 — 计算节点在启动时自动针对 LDAP 服务器进行身份验证,无需在启动模板中进行额外配置。
预部署的 AMI 是使用 EC2 Image Builder 通过一个自定义组件构建的,该组件按顺序运行以下步骤:
module load openmpi5 可在每个节点上运行)这与https://github.com/aws-samples/sample-parallel-computing-service Terraform 仓库中的模式相同,该仓库提供了一个可用的参考实现。
我们将创建一个 EC2 启动模板,用于告知 PCS 如何配置计算节点实例。启动模板控制 EFA 网络、文件系统挂载和placement group配置。
PCS 使用 EC2 启动模板来配置它为计算节点组启动的实例。启动模板定义了:
PCS 启动模板中的用户数据必须采用 MIME 多部分归档格式。这是因为 PCS 在节点注册期间会将我们的用户数据与其自身的内部用户数据合并。
首先,确定哪个可用区拥有 hpc7a 实例,并使用匹配的子网:
export INFRA_STACK="pcs-workshop-infra"
export AWS_DEFAULT_REGION="us-east-2"
export HPC_SUBNET=$(aws cloudformation describe-stacks \
--stack-name $INFRA_STACK \
--query "Stacks[0].Outputs[?OutputKey=='HpcSubnetId'].OutputValue" \
--output text)
export PRIVATE_SG=$(aws cloudformation describe-stacks \
--stack-name $INFRA_STACK \
--query "Stacks[0].Outputs[?OutputKey=='PrivateSecurityGroupId'].OutputValue" \
--output text)
export EFA_SG=$(aws cloudformation describe-stacks \
--stack-name $INFRA_STACK \
--query "Stacks[0].Outputs[?OutputKey=='EfaSecurityGroupId'].OutputValue" \
--output text)
export EFS_ID=$(aws cloudformation describe-stacks \
--stack-name $INFRA_STACK \
--query "Stacks[0].Outputs[?OutputKey=='EfsFileSystemId'].OutputValue" \
--output text)
export FSX_ID=$(aws cloudformation describe-stacks \
--stack-name $INFRA_STACK \
--query "Stacks[0].Outputs[?OutputKey=='FsxLustreFileSystemId'].OutputValue" \
--output text)
export FSX_MOUNT=$(aws cloudformation describe-stacks \
--stack-name $INFRA_STACK \
--query "Stacks[0].Outputs[?OutputKey=='FsxLustreMountName'].OutputValue" \
--output text)
export FSX_DNS="${FSX_ID}.fsx.${AWS_DEFAULT_REGION}.amazonaws.com"
echo "HPC Subnet: $HPC_SUBNET"
echo "Private SG: $PRIVATE_SG"
echo "EFA SG: $EFA_SG"
echo "EFS ID: $EFS_ID"
echo "FSx DNS: $FSX_DNS"
echo "FSx Mount: $FSX_MOUNT"
cloudformation的 HpcSubnetId 输出是位于 hpc7a 实例可用的可用区中的私有子网。CloudFormation 模板使用 Lambda function 自动检测此项。
placement group是一项 EC2 功能,可影响实例在数据中心中的物理位置分布。对于使用 EFA 的 HPC 工作负载,我们需要一个 cluster placement group — 它将所有实例放置在物理上彼此靠近的硬件上,从而最小化节点之间的网络延迟。
如果没有placement group,我们的实例可能会分布在数据中心中相距很远的机架上,为每条 MPI 消息增加数微秒的延迟。对于紧耦合的并行工作负载,这一点很重要。
aws ec2 create-placement-group \
--group-name hpc-cluster-pg \
--strategy cluster \
--region $AWS_DEFAULT_REGION
cluster 策略将实例尽可能紧密地打包在一起。我们在启动模板中引用此placement group,以便 PCS 启动的每个计算节点都位于同一个物理集群中。
EC2 user data是一个脚本(或一组脚本),在实例首次启动时自动运行。PCS 要求用户数据采用 MIME 多部分归档格式 — 这是因为 PCS 会注入其自身的用户数据用于节点注册,而 MIME 格式允许将多个脚本组合在一起。
我们的 HPC 计算节点的user data执行两项操作:
对于 hpc7a 实例,Lustre 可以使用 EFA 而不是 TCP 进行数据传输。这需要配置 Lustre 网络层(lnet)以通过 EFA 接口路由流量,从而提供比标准 TCP 显著更好的 I/O 性能。
创建用户数据文件:
cat > hpc-userdata.txt << USERDATA
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==//=="
--==//==
Content-Type: text/x-shellscript; charset="us-ascii"
MIME-Version: 1.0
#!/bin/bash
# Mount EFS at /home
echo "${EFS_ID}.efs.${AWS_DEFAULT_REGION}.amazonaws.com:/ /home nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,_netdev 0 0" >> /etc/fstab
mount /home
# Configure Lustre over EFA for hpc7a
modprobe lnet
modprobe kefalnd ipif_name=enp34s0
modprobe ksocklnd
lnetctl lnet configure
lnetctl net del --net tcp
lnetctl net add --net tcp --if enp34s0
lnetctl net add --net efa --if rdmap36s0 --peer-credits 32
lnetctl net add --net efa --if rdmap42s0 --peer-credits 32
lnetctl set discovery 1
lnetctl udsp add --src efa --priority 0
modprobe lustre
# Mount FSx Lustre at /fsx
echo "${FSX_DNS}@tcp:/${FSX_MOUNT} /fsx lustre defaults,_netdev,flock,user_xattr,noatime 0 0" >> /etc/fstab
mkdir -p /fsx
chmod a+rwx /fsx
mount /fsx
chmod 777 /fsx
# Configure Enroot for container support
# Cache on Lustre (shared across nodes), everything else on EBS (needs overlayfs xattr support)
cat > /etc/enroot/enroot.conf << 'ENROOTCFG'
ENROOT_RUNTIME_PATH /var/tmp/enroot/runtime
ENROOT_CONFIG_PATH /var/tmp/enroot/config
ENROOT_CACHE_PATH /fsx/enroot/cache
ENROOT_DATA_PATH /var/tmp/enroot/data
ENROOT_TEMP_PATH /var/tmp/enroot/tmp
ENROOTCFG
mkdir -p /var/tmp/enroot/runtime /var/tmp/enroot/config /var/tmp/enroot/data /var/tmp/enroot/tmp /fsx/enroot/cache
chmod 1777 /var/tmp/enroot /var/tmp/enroot/runtime /var/tmp/enroot/config /var/tmp/enroot/data /var/tmp/enroot/tmp
chmod 1777 /fsx/enroot/cache
# Remove NVIDIA container hook (CPU-only workshop)
rm -f /etc/enroot/hooks.d/98-nvidia.sh
--==//==
USERDATA
lnetctl 命令将 Lustre 网络配置为使用 EFA 而不是 TCP。在 hpc7a.96xlarge 实例上,enp34s0 是主网络接口,而 rdmap36s0/rdmap42s0 是两个 EFA 接口。lnetctl udsp add --src efa --priority 0 告知 Lustre 优先使用 EFA 而不是 TCP 进行数据传输,从而提供显著更好的 I/O 性能。
EFS 挂载使用 NFSv4.1 并采用较大的读/写大小(1 MB)以实现最佳吞吐量。_netdev 选项确保挂载等待网络可用。
SSSD/LDAP 身份验证已在 AMI 中配置 — 我们无需向用户数据添加 LDAP 配置。计算节点将在启动时自动针对 LDAP 服务器对用户进行身份验证。
EC2 启动模板是一个蓝图,定义了实例在启动时应如何配置。PCS 使用启动模板为它创建的每个计算节点设置网络、安全和启动时配置。
我们无需在启动模板中指定实例类型或 IAM 配置文件 — PCS 在我们创建计算节点组时会设置这些内容。
启动模板处理其他所有内容:网络接口、放置、安全组和用户数据。
对于 hpc7a.96xlarge,关键的网络细节是它具有 2 张网卡,每张都能运行 EFA。我们在启动模板中为每张网卡配置一个 EFA 接口。
为我们即将创建的启动模板设置 EC2 密钥对。cloudformation会在我们的账户中自动创建 ws-default-keypair,我们也可以使用自己创建的其他keypair,请在运行此代码块之前覆盖 KEY_NAME。该变量不得为空 — 空的 KeyName 会生成一个启动模板,当 PCS 启动实例时会导致验证失败。
export KEY_NAME="${KEY_NAME:-ws-default-keypair}"
创建启动模板:
PG_ID=$(aws ec2 describe-placement-groups \
--group-names hpc-cluster-pg \
--query "PlacementGroups[0].GroupId" \
--output text \
--region $AWS_DEFAULT_REGION)
aws ec2 create-launch-template \
--launch-template-name hpc-compute-lt \
--launch-template-data "{
\"KeyName\": \"${KEY_NAME}\",
\"MetadataOptions\": {
\"HttpEndpoint\": \"enabled\",
\"HttpPutResponseHopLimit\": 2,
\"HttpTokens\": \"required\"
},
\"Monitoring\": {\"Enabled\": true},
\"Placement\": {\"GroupId\": \"${PG_ID}\"},
\"NetworkInterfaces\": [
{
\"DeviceIndex\": 0,
\"NetworkCardIndex\": 0,
\"InterfaceType\": \"efa\",
\"Groups\": [\"${PRIVATE_SG}\", \"${EFA_SG}\"]
},
{
\"DeviceIndex\": 1,
\"NetworkCardIndex\": 1,
\"InterfaceType\": \"efa\",
\"Groups\": [\"${PRIVATE_SG}\", \"${EFA_SG}\"]
}
],
\"UserData\": \"$(base64 -w 0 hpc-userdata.txt 2>/dev/null || base64 -i hpc-userdata.txt | tr -d '\n')\"
}" \
--region $AWS_DEFAULT_REGION
以下是该配置的作用:
ws-default-keypair。如果我们自行部署,请在运行上面的代码块之前使用我们账户中现有密钥对的名称覆盖 KEY_NAME。--subnet-ids 参数设置子网。169.254.169.254 的本地 HTTP 端点,实例上的进程用它来发现自己的身份和 IAM 凭证。当在启动模板中定义网络接口时,我们必须在每个接口的 Groups 字段中指定安全组 — 我们不能使用顶层的 SecurityGroupIds 参数。
在ec2的launch template中看到这个模板:

保存启动模板 ID 以后面使用:
export HPC_LT_ID=$(aws ec2 describe-launch-templates \
--launch-template-names hpc-compute-lt \
--query "LaunchTemplates[0].LaunchTemplateId" \
--output text \
--region $AWS_DEFAULT_REGION)
echo "Launch Template ID: $HPC_LT_ID"
我们已经创建了一个启动模板,它为 hpc7a 实例配置 EFA 网络,并挂载 FSx Lustre(通过 EFA)和 EFS 文件系统。接下来,我们将创建一个使用此模板的计算节点组。