使用 EFS 在应用服务器之间共享文件

AWSBeginner
立即练习

介绍

上传应用运行在两台服务器上。它们目前分别将文件保存在本地磁盘,因此用户可能通过一台服务器上传成功,却在另一台服务器得到“文件不存在”。使用 Amazon Elastic File System(EFS)为两台服务器提供共享文件接口。

请先完成 AWS VPC 入门和 AWS EC2、EBS 入门。本独立环境提供上传应用、三台应用主机及其网络。在 Terminal 中执行 AWS CLI 和 SSH 命令,在 AWS View 中比较真实上传响应。CLI 已配置,无需个人 AWS 账号或之前实验的资源。本实验聚焦文件共享和连接,不测试生产性能或可用性。

认证关联

认证 考纲任务 实践
Cloud Practitioner (CLF-C02) Task 3.6 认识 EFS 共享文件服务,区分挂载的文件接口与各自独立的本地上传目录。

课程概览

概念示意图;实际标识符以你的新环境为准。

为上传应用创建存储

本步骤先观察本地上传的问题,再创建共享文件系统。

在 /home/labex/project 中操作。确认配置的身份,不显示凭据:

cd /home/labex/project
aws sts \
  get-caller-identity

用户 ARN 以 labex-fs01-operator 结尾。resources.json 记录准备好的子网和应用安全组。读取这些标识符,让后续命令使用当前环境的真实资源:

SUBNET_ID=$(jq -r .subnet_id resources.json)
APP_GROUP_ID=$(jq -r .application_group_id resources.json)
VPC_ID=$(jq -r .vpc_id resources.json)
echo "$SUBNET_ID"
echo "$APP_GROUP_ID"
echo "$VPC_ID"
aws ec2 \
  describe-instances \
  --filters Name=tag:Name,Values=application-a,application-b \
  --query 'Reservations[].Instances[].{Name:Tags[?Key==`Name`].Value|[0],IP:PrivateIpAddress,State:State.Name}'

先观察本地存储问题。HTTP PUT 保存上传内容,HTTP GET 读取文件。以下地址连接准备好的应用服务器:

curl -i -X PUT --data-binary 'A local upload' \
  http://127.0.0.1:8081/uploads/local-example.txt
curl -i http://127.0.0.1:8082/uploads/local-example.txt

A 返回 201 Created,B 返回 404 Not Found,因为 B 的本地目录包含另一组文件。也可以通过 AWS View 的两个应用卡片写入、读取同一文件名。

EFS 提供共享文件系统:两个应用通过网络文件系统协议 NFS 访问相同目录和文件。先创建文件系统。创建令牌标识这次创建请求,Name 标签便于检查和清理时识别资源。--encrypted 启用服务的静态加密设置;传输加密将在挂载时另行配置。

FS_ID=$(aws efs \
  create-file-system \
  --creation-token fs01-shared-uploads \
  --encrypted \
  --tags Key=Name,Value=fs01-shared-uploads \
  --query FileSystemId --output text)
echo "$FS_ID"
aws efs \
  describe-file-systems \
  --file-system-id "$FS_ID" \
  --query 'FileSystems[].{ID:FileSystemId,State:LifeCycleState,Encrypted:Encrypted}'

等待状态为 available 再继续。这说明服务资源已存在,但应用还需要可达的挂载目标和真正的挂载。

将文件系统连接到应用子网

本步骤提供可达的 NFS 端点,并将访问限制为应用服务器。

挂载目标提供文件系统在子网内的网络端点。NFS 使用 TCP 2049 端口。创建专用存储安全组,并允许准备好的应用安全组访问该端口。来源组规则包含两台应用服务器,不会向所有地址开放 NFS。无关参考服务器属于另一组,应继续被排除。

EFS_GROUP_ID=$(aws ec2 \
  create-security-group \
  --group-name fs01-efs-access \
  --description 'NFS from the upload application servers' \
  --vpc-id "$VPC_ID" \
  --query GroupId --output text)
echo "$EFS_GROUP_ID"
aws ec2 \
  authorize-security-group-ingress \
  --group-id "$EFS_GROUP_ID" \
  --ip-permissions "IpProtocol=tcp,FromPort=2049,ToPort=2049,UserIdGroupPairs=[{GroupId=$APP_GROUP_ID}]"

在准备好的应用子网中创建挂载目标,并绑定新的安全组:

MOUNT_TARGET_ID=$(aws efs \
  create-mount-target \
  --file-system-id "$FS_ID" \
  --subnet-id "$SUBNET_ID" \
  --security-groups "$EFS_GROUP_ID" \
  --query MountTargetId --output text)
echo "$MOUNT_TARGET_ID"
aws efs \
  describe-mount-targets \
  --file-system-id "$FS_ID" \
  --query 'MountTargets[].{ID:MountTargetId,IP:IpAddress,State:LifeCycleState,AZ:AvailabilityZoneName}'
aws efs \
  describe-mount-target-security-groups \
  --mount-target-id "$MOUNT_TARGET_ID"

等待挂载目标为 available。本实验将两台应用放在一个可用区,因此准备好的拓扑只需一个挂载目标。生产区域部署应为客户端所在的各可用区规划挂载目标。

挂载两个应用并观察共享文件

本步骤将两个应用连接到 EFS,测试双向上传。

提供的 ssh_config 用于连接各应用主机。挂载点是应用访问远程文件系统的本地目录。在两台主机上创建 /srv/shared,并将 EFS 根目录挂载到这里:

ssh -F ssh_config application-a \
  "sudo mkdir -p /srv/shared && sudo mount -t efs -o tls,stunnel,lookupcache=positive ${FS_ID}:/ /srv/shared"
ssh -F ssh_config application-b \
  "sudo mkdir -p /srv/shared && sudo mount -t efs -o tls,stunnel,lookupcache=positive ${FS_ID}:/ /srv/shared"

挂载使用 lookupcache=positive:当另一台应用创建之前不存在的文件时,再次向服务器查询该文件名,让短时间内的上传结果可见。生产工作负载的缓存调整还需要单独评估性能。参见 Linux NFS 目录缓存选项。

-t efs 选择官方 EFS 挂载助手。tls 加密连接,stunnel 选择其支持的 TLS 传输方式。${FS_ID}:/ 选择文件系统根目录。准备好的主机已有所需客户端工具,无需另行安装。

新文件系统的目录最初由 root 拥有。为应用的 labex 用户准备上传子目录,并让两个应用使用它:

ssh -F ssh_config application-a \
  'sudo mkdir -p /srv/shared/uploads && sudo chown labex:labex /srv/shared/uploads'
ssh -F ssh_config application-a \
  'printf "{\"upload_directory\":\"/srv/shared/uploads\"}\n" | sudo tee /etc/uploads-app/config.json'
ssh -F ssh_config application-b \
  'printf "{\"upload_directory\":\"/srv/shared/uploads\"}\n" | sudo tee /etc/uploads-app/config.json'

应用在每次请求时读取配置,无需重启。查看两台主机的实际挂载:

ssh -F ssh_config application-a 'findmnt -t nfs4 /srv/shared'
ssh -F ssh_config application-b 'findmnt -t nfs4 /srv/shared'

两台主机都在 /srv/shared 显示 NFS 4 挂载。使用 TLS 时,内核显示的源可能是 127.0.0.1:/,因为挂载助手通过本地加密隧道转发 NFS。

现在通过 A 上传,再通过 B 读取:

curl -i -X PUT --data-binary 'Shared upload version 1' \
  http://127.0.0.1:8081/uploads/shared.txt
curl -i http://127.0.0.1:8082/uploads/shared.txt

B 返回 200 OK 和 Shared upload version 1。再通过 B 更新、通过 A 读取,证明共享是双向的:

curl -i -X PUT --data-binary 'Shared upload version 2' \
  http://127.0.0.1:8082/uploads/shared.txt
curl -i http://127.0.0.1:8081/uploads/shared.txt
ssh -F ssh_config application-a 'stat -c "%i %u:%g" /srv/shared/uploads/shared.txt'
ssh -F ssh_config application-b 'stat -c "%i %u:%g" /srv/shared/uploads/shared.txt'

两个应用都读取版本 2,两条 stat 命令报告相同的文件 inode。EFS 通过独立挂载提供同一个共享文件,不是同步或复制两个本地上传目录。在 AWS View 中比较同一上传,作为浏览器观察点。

AWS View shared uploads

两台应用读取同一份更新后的上传内容。资源标识符以每次新环境为准。

卸载并删除自有存储

本步骤停止使用共享目录,仅删除你创建的存储资源。

删除网络端点前先停止使用共享目录。将每个应用恢复到提供的本地上传目录,然后卸载 EFS:

ssh -F ssh_config application-a \
  'printf "{\"upload_directory\":\"/srv/uploads\"}\n" | sudo tee /etc/uploads-app/config.json'
ssh -F ssh_config application-b \
  'printf "{\"upload_directory\":\"/srv/uploads\"}\n" | sudo tee /etc/uploads-app/config.json'
ssh -F ssh_config application-a 'sudo umount /srv/shared'
ssh -F ssh_config application-b 'sudo umount /srv/shared'

如果 umount 提示目录忙,请退出正在使用 /srv/shared 的 Shell 后重试。确认挂载已消失:

ssh -F ssh_config application-a 'findmnt -t nfs4'
ssh -F ssh_config application-b 'findmnt -t nfs4'

不应再有 NFS 挂载记录。没有匹配挂载时 findmnt 返回状态 1,在这里属于预期结果。

先删除挂载目标,再删除文件系统,最后仅删除你的专用存储安全组:

aws efs \
  delete-mount-target --mount-target-id "$MOUNT_TARGET_ID"
aws efs \
  describe-mount-targets --file-system-id "$FS_ID"

等待 MountTargets 为空,再继续:

aws efs \
  delete-file-system --file-system-id "$FS_ID"
aws ec2 \
  delete-security-group --group-id "$EFS_GROUP_ID"
aws efs \
  describe-file-systems \
  --query 'FileSystems[?Name==`fs01-shared-uploads`].FileSystemId'
aws ec2 \
  describe-security-groups \
  --filters Name=group-name,Values=fs01-efs-access \
  --query 'SecurityGroups[].GroupId'

两条查询都返回空列表。成功的服务查询才能证明删除;连接或授权错误不能证明资源已删除。保留准备好的应用服务器、它们的安全组和子网。

运行本步骤检查,确认存储已删除、应用准备资源仍然保留。后续每个实验都从各自独立的准备环境开始。

总结

你通过挂载目标、受限 NFS 访问和 TLS 挂载,将两个应用连接到同一个 EFS 文件系统。双向上传和相同文件身份证明了真实共享。随后将应用恢复到本地存储,卸载客户端,并按依赖顺序删除自有存储资源。

共享文件系统解决文件位置问题,但不会自动分隔应用目录或选择 POSIX 身份。下一个引导实验将使用 EFS 访问点建立这些边界。

进一步阅读 AWS 官方资料:EFS 挂载助手与 TLS 传输; 客户端与挂载目标网络规则。