创建 AWS EFS
本文基本是基于此efs workshop的记录和扩展。
要创建一个 EFS 资源,大致有以下几个步骤:
要在哪个 VPC 上创建 –> 这个 VPC 上子网的 CIDR
创建一个 SG –> 设置这个 SG 的 ingress rule: 对子网开放 NFS 的 2049
创建 EFS,根据需求设置不同的参数比如是否加密、备份、performance mode、throughput-mode 等。
找到 VPC 上的 public subnet,在这些 public subnet 上创建 Moint Target。
有了 mount targets,这个 NFS 就已经可以对外提供服务了。
如果需要对 mount 的网络文件系统的目录设置特定的 user、group 属性,那么可以通过在这个 NFS 上创建 Access Points 完成。
因为 EFS 是可以跨 region 在这个 region 的所有 AZ 中可用的一个 NFS,所以需要 VPC ID 应该是比较容易理解的。
下面介绍一下如何通过 aws cli 创建 EFS 及其 Access Points,完整的脚本可以在这里下载 create_efs.sh, create_access_points.sh。
设置参数
这些变量定义了我们当前的 aws 环境以及要创建的资源名称等信息。
首先我们可以设置一些变量定义当前环境
AWS_PROFILE=myProfile
AWS_REGION=us-west-2
CLUSTER_NAME=myCluster
设置中间过程中会用到的常量
MOUNT_TARGET_GROUP_NAME=mySG4EFS
MOUNT_TARGET_GROUP_DESC="NFS access to EFS from EKS worker nodes"
EFS_NAME=myEfsName
1)获取 VPC ID
因为这里创建出来的 EFS 要供 EKS 的 pod 使用,所以 VPC 的获取是根据 eks cluster 得到的。
VPC_ID=$(aws eks describe-cluster --profile $AWS_PROFILE --region $AWS_REGION --name $CLUSTER_NAME \
--query "cluster.resourcesVpcConfig.vpcId" --output text)
echo "The $CLUSTER_NAME includes the VPC $VPC_ID"
2)获取 VPC 下的 CIDR
CIDR_BLOCK=$(aws ec2 describe-vpcs --profile $AWS_PROFILE --region $AWS_REGION \
--vpc-ids $VPC_ID --query "Vpcs[].CidrBlock" --output text)
echo "The CIDR blocks in the $VPC_ID : $CIDR_BLOCK"
3)在 VPC 上创建 Security Group
MOUNT_TARGET_GROUP_ID=$(aws ec2 create-security-group --profile $AWS_PROFILE --region $AWS_REGION \
--group-name $MOUNT_TARGET_GROUP_NAME \
--description "$MOUNT_TARGET_GROUP_DESC" \
--vpc-id $VPC_ID \
| jq --raw-output '.GroupId')
4)设置去安全组的 ingres 对 2049 端口开放
aws ec2 authorize-security-group-ingress --profile $AWS_PROFILE --region $AWS_REGION \
--group-id $MOUNT_TARGET_GROUP_ID --protocol tcp --port 2049 --cidr $CIDR_BLOCK
5)创建 EFS
aws efs create-file-system
命令本身并没有选项用于设置资源名称,而是通过 Tag key=Name 首先的。这里要注意 Name 单词的大小写,使用小写的 name 并不能设置 efs name。
通过使用 creation-token 来做到创建操作的等幂性。如果你的系统希望 efs 资源的 name 是唯一的,那么的选择使用 efs 的名称作为 creation-token 是个不错的选择。
FILE_SYSTEM_ID=$(aws efs create-file-system --profile $AWS_PROFILE --region $AWS_REGION \
--performance-mode generalPurpose --throughput-mode bursting \
--tags Key=Name,Value=$EFS_NAME \
--backup --encrypted --creation-token "$EFS_NAME"_0 | jq --raw-output '.FileSystemId')
echo "The EFS $FILE_SYSTEM_ID is created."
查看某个 efs:
aws efs describe-file-systems --file-system-id $FILE_SYSTEM_ID
EFS 资源已经创建出来了,要让它能被使用就需要把它 mount 到 VPC 的 public subnets 上。
一个 subnet 是 public 的还是 private 的,并不是通过 subnet 对象的某个属性标识的,而是要看路由表里这个 subnet 有没有通向 0.0.0.0 的 internet gateway。下面的几个步骤就用于找到 public subnet 并把 EFS mount 到这些 public subnets。
6) 得到 eks 里的 subnetIds
eksSubnetIds=($(aws eks describe-cluster --profile $AWS_PROFILE --region $AWS_REGION \
--name $CLUSTER_NAME --query "cluster.resourcesVpcConfig.subnetIds" \
--output text))
echo "The eks cluster $CLUSTER_NAME VPC $VPC_ID includes the subnets: $eksSubnetIds"
7) 找到 internet gateway
IGW_ID=$(aws ec2 describe-internet-gateways --profile $AWS_PROFILE --region $AWS_REGION \
--filters Name=attachment.vpc-id,Values=${VPC_ID} \
--query "InternetGateways[].InternetGatewayId" \
| jq -r '.[0]')
echo "The internet gateway in the VPC $VPC_ID is $IGW_ID"
if [ "null" = "$IGW_ID" ] ; then
echo "Can't find public IGW in VPN, exit ..."
fi
8) 找到 public subnets
for subnetId in ${eksSubnetIds[@]}
do
echo "Check the subnet " $subnetId
IGW_IN_ROUTS=$(aws ec2 describe-route-tables --profile $AWS_PROFILE --region $AWS_REGION \
--filter Name=association.subnet-id,Values=$subnetId \
--query "RouteTables[].Routes[]" \
| jq -r '.[] | select(.DestinationCidrBlock=="0.0.0.0/0") | .GatewayId')
if [ -z $IGW_IN_ROUTS -o "null" = $IGW_IN_ROUTS ] ; then
echo "The subnet $subnetId is a private subnet."
else
echo "The subnet $subnetId is a public subnet. $IGW_ID $IGW_IN_ROUTS"
if [ "$IGW_ID" = "$IGW_IN_ROUTS" ] ; then
echo "Creating the mount target in the subnet $subnetId."
aws efs create-mount-target --profile $AWS_PROFILE --region $AWS_REGION \
--file-system-id $FILE_SYSTEM_ID \
--subnet-id $subnetId \
--security-groups $MOUNT_TARGET_GROUP_ID
elif [ "null" != "$IGW_IN_ROUTS" ] ; then
echo "WARNING: The IGW id in routes does not equal with the one in VPC!"
fi
fi
done
10) 创建 Access Point
到这里这个 NFS 已经可以在这个 VPC 里提供服务了。如果你的目录需要更精细的用户、组的设置,可以通过下面的命令创建 Access Point 来做更精细的控制。
ACCESS_POING_NAME=myAP
FILE_SYSTEM_ID=fs-082697b352a3230d1
AP_USER='{"Uid": 123, "Gid": 123, "SecondaryGids": [20]}'
AP_ROOT_DIR='/myapp/logs,CreationInfo={OwnerUid=123,OwnerGid=123,Permissions=0755}'
aws efs create-access-point --profile $AWS_PROFILE --region $AWS_REGION \
--tags Key=name,Value=$ACCESS_POING_NAME \
--client-token "$ACCESS_POING_NAME" \
--file-system-id $FILE_SYSTEM_ID \
--posix-user $AP_USER \
--root-directory Path=$AP_ROOT_DIR
下面显示的是在 eks 中通过 StorageClass 自动分配 EFS 资源的场景下,如何如何设置相关属性。 参考 https://github.com/kubernetes-sigs/aws-efs-csi-driver 查看完整的 parameter 列表。
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: efs-sc
provisioner: efs.csi.aws.com
mountOptions:
- tls
- iam
parameters:
provisioningMode: efs-ap
fileSystemId: fs-012345678901010
directoryPerms: "700"
gidRangeStart: "1000"
gidRangeEnd: "2000"
basePath: "/dynamic_provisioning"
Reference:
[1]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/file-storage.html
[2]: https://docs.aws.amazon.com/efs/latest/ug/creating-using.html
[3]: https://docs.aws.amazon.com/cli/latest/reference/efs/create-file-system.html
[4]: https://docs.aws.amazon.com/cli/latest/reference/efs/create-access-point.html
[5]: https://docs.aws.amazon.com/efs/latest/ug/creating-using-create-fs.html#creating-using-fs-part1-cli
[6]: https://stackoverflow.com/questions/48830793/aws-vpc-identify-private-and-public-subnet
[7]: https://www.baeldung.com/linux/jq-command-json
[8]: https://aws.amazon.com/premiumsupport/knowledge-center/eks-troubleshoot-efs-volume-mount-issues/
[9]: https://github.com/kubernetes-sigs/aws-efs-csi-driver