Введение
Приложение загрузки работает на двух серверах. Каждый сохраняет файлы на своём локальном диске: файл может успешно загрузиться через один сервер, но не найтись на другом. Предоставьте обоим общий файловый интерфейс с помощью Amazon Elastic File System (EFS).
Сначала завершите AWS VPC for Beginners и AWS EC2 and EBS for Beginners. Независимая среда содержит приложение, три узла и их сеть. Используйте 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 сохраняет загрузку, 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: его локальный каталог содержит другие файлы. Можно также записать и прочитать одинаковое имя через две карточки AWS View.
EFS предоставляет общую файловую систему. Оба приложения обращаются к одним каталогам и файлам через протокол Network File System (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. Ресурс существует, но приложениям ещё нужны доступная цель монтирования и настоящий mount.
Подключить систему к подсети приложений
На этом шаге создайте доступную точку 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 выбирает официальный помощник монтирования, 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'
Оба узла показывают NFS 4 в /srv/shared. При 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.

Оба приложения читают один обновлённый файл. Идентификаторы различаются в новых средах.
Размонтировать и удалить своё хранилище
На этом шаге прекратите использовать общий каталог и удалите только созданное вами хранилище.
Перед удалением сетевой точки верните каждому приложению подготовленный локальный каталог и размонтируйте 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, и повторите. Проверьте отсутствие монтирований:
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'
Оба запроса возвращают пустые списки. Удаление подтверждает успешный авторизованный запрос; ошибка сети или разрешений этого не доказывает. Сохраните подготовленные серверы, их группы и подсеть.
Запустите проверку удаления и сохранности подготовленных ресурсов. Каждое последующее упражнение начинается в собственной независимой среде.
Резюме
Вы подключили два приложения к одному EFS через цель монтирования, ограниченный NFS-доступ и TLS. Взаимные загрузки и одинаковая идентичность файла доказали реальный общий доступ. Затем вы вернули локальное хранилище, размонтировали клиенты и удалили свои ресурсы в порядке зависимостей.
Общая файловая система не разделяет каталоги приложений и POSIX-идентичности автоматически. Следующее упражнение устанавливает эти границы с помощью точек доступа EFS.
Подробнее см. в документации AWS: Помощник монтирования EFS и транспорт TLS; Сетевые правила клиентов и целей монтирования.



