액세스 포인트로 애플리케이션 디렉터리 분리하기

AWSBeginner
지금 연습하기

소개

두 애플리케이션이 공유 저장소를 필요로 하지만 각자 자신의 디렉터리에서 작업해야 합니다. EFS 액세스 포인트로 마운트별 루트와 POSIX 신원을 설정하고 실제 파일과 소유권 경계를 확인합니다.

먼저 EFS로 애플리케이션 서버 간 파일 공유하기를 완료하세요. 독립된 새 VM에는 새 호스트와 서브넷이 있으며 이전 파일 시스템을 재사용하지 않습니다. Terminal에서 설정된 AWS CLI와 SSH를 사용하고 AWS View에서 응답을 비교합니다. 개인 AWS 계정은 필요하지 않습니다.

자격증 연계

자격증 시험 항목 실습
Cloud Practitioner (CLF-C02) Task 3.6 EFS 디렉터리와 파일 권한을 살펴보고 객체 저장소와 비교합니다.

개요

개념도입니다. 실제 식별자는 새 환경에서 확인합니다.

공유 파일 시스템과 NFS 연결 지점 준비하기

이 단계에서는 두 애플리케이션이 사용할 저장소와 제한된 연결 지점을 만듭니다.

/home/labex/project에서 준비된 네트워크를 확인하세요. AWS CLI는 설정되어 있습니다.

cd /home/labex/project
aws sts \
  get-caller-identity
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"

새 EFS 파일 시스템을 만듭니다. 두 애플리케이션은 같은 리소스를 사용하고 액세스 포인트는 서로 다른 디렉터리를 선택합니다.

FS_ID=$(aws efs \
  create-file-system \
  --creation-token fs02-application-files \
  --encrypted \
  --tags Key=Name,Value=fs02-application-files \
  --query FileSystemId --output text)
echo "$FS_ID"

전용 저장소 그룹을 만듭니다.

EFS_GROUP_ID=$(aws ec2 \
  create-security-group \
  --group-name fs02-efs-access \
  --description 'NFS for the upload application servers' \
  --vpc-id "$VPC_ID" \
  --query GroupId --output text)
echo "$EFS_GROUP_ID"

애플리케이션 그룹에서 TCP 2049를 허용합니다. 이 규칙은 NFS 연결을 허용하지만 디렉터리나 사용자 신원을 선택하지 않습니다.

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"

리소스 상태를 조회하고 두 항목 모두 available이 될 때까지 기다립니다.

aws efs \
  describe-file-systems \
  --file-system-id "$FS_ID" \
  --query 'FileSystems[].{ID:FileSystemId,State:LifeCycleState}'
aws efs \
  describe-mount-targets \
  --file-system-id "$FS_ID" \
  --query 'MountTargets[].{ID:MountTargetId,IP:IpAddress,State:LifeCycleState}'

애플리케이션별 액세스 포인트 만들기

이 단계에서는 각 마운트의 루트와 파일 작업 신원을 정의합니다.

액세스 포인트는 애플리케이션용 EFS 진입점입니다. 루트는 해당 마운트에서 /가 되고 POSIX 사용자가 클라이언트 파일 작업 신원을 대신합니다. POSIX는 숫자 UID로 사용자를, GID로 그룹을 식별합니다.

A의 포인트를 만듭니다.

AP_A_ID=$(aws efs \
  create-access-point \
  --file-system-id "$FS_ID" \
  --posix-user Uid=2001,Gid=2001 \
  --root-directory 'Path=/app-a,CreationInfo={OwnerUid=2001,OwnerGid=2001,Permissions=700}' \
  --tags Key=Name,Value=fs02-app-a \
  --query AccessPointId --output text)
echo "$AP_A_ID"

/app-a가 없으면 CreationInfo가 소유자와 권한을 설정합니다. 700은 소유자에게 읽기, 쓰기와 디렉터리 통과를 허용하고 다른 사용자에게는 허용하지 않습니다. 액세스 포인트 UID/GID는 소유자와 같습니다.

다른 루트와 신원을 사용하는 두 번째 포인트를 만듭니다.

AP_B_ID=$(aws efs \
  create-access-point \
  --file-system-id "$FS_ID" \
  --posix-user Uid=2002,Gid=2002 \
  --root-directory 'Path=/app-b,CreationInfo={OwnerUid=2002,OwnerGid=2002,Permissions=700}' \
  --tags Key=Name,Value=fs02-app-b \
  --query AccessPointId --output text)
echo "$AP_B_ID"

두 정의를 확인합니다.

aws efs \
  describe-access-points \
  --file-system-id "$FS_ID" \
  --query 'AccessPoints[].{ID:AccessPointId,Root:RootDirectory.Path,User:PosixUser,Creation:RootDirectory.CreationInfo}'

루트는 /app-a와 /app-b, 신원은 2001:2001과 2002:2002입니다. 다음 단계에서 실제 동작을 확인합니다. 설정만으로 경계를 증명할 수는 없습니다.

포인트는 그 포인트를 선택한 마운트의 루트와 신원을 제한합니다. 허가된 클라이언트가 다른 포인트를 선택하는 것을 단독으로 막지는 않습니다. 운영 환경에서는 IAM과 파일 시스템 정책으로 허용된 포인트를 제한할 수 있습니다. 이 실습은 디렉터리와 POSIX 동작을 다룹니다.

디렉터리 분리와 강제 소유권 관찰하기

이 단계에서는 각 포인트를 마운트하고 같은 이름이 다른 파일을 나타냄을 확인합니다.

A에 A의 포인트를 마운트합니다. 액세스 포인트 마운트에는 TLS가 필요합니다.

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

lookupcache=positive는 다른 애플리케이션이 파일을 만들 때 이전에 없던 이름을 서버에 다시 조회합니다. 짧은 실습에서 결과를 확인할 수 있게 하며 운영 환경의 캐시 조정은 성능을 따로 평가해야 합니다. Linux NFS 디렉터리 캐시 옵션을 참고하세요.

B에 B의 포인트를 마운트합니다.

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

로컬 경로는 둘 다 /srv/shared지만 A에서는 /app-a, B에서는 /app-b를 나타냅니다. 각 애플리케이션이 자신의 포인트 루트를 직접 사용하도록 설정합니다.

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

A에서 private.txt를 쓰고 B에서 읽어 봅니다.

curl -i -X PUT --data-binary 'Private upload for application A' \
  http://127.0.0.1:8081/uploads/private.txt
curl -i http://127.0.0.1:8082/uploads/private.txt

A는 201 Created, B는 404 Not Found를 반환합니다. 이제 같은 이름으로 B 자신의 파일을 만듭니다.

curl -i -X PUT --data-binary 'Private upload for application B' \
  http://127.0.0.1:8082/uploads/private.txt
curl -i http://127.0.0.1:8081/uploads/private.txt
curl -i http://127.0.0.1:8082/uploads/private.txt

A는 A의 내용, B는 B의 내용을 반환합니다. AWS View 두 카드에서 private.txt와 Read upload를 선택하세요.

실제 마운트와 파일 신원을 조회합니다.

ssh -F ssh_config application-a 'findmnt -t nfs4 /srv/shared; stat -c "%i %u:%g" /srv/shared/private.txt'
ssh -F ssh_config application-b 'findmnt -t nfs4 /srv/shared; stat -c "%i %u:%g" /srv/shared/private.txt'

inode가 다르고 소유자는 2001:2001과 2002:2002입니다. 애플리케이션은 labex로 실행되지만 서버는 액세스 포인트 신원을 적용합니다.

root가 A의 마운트를 통해 써도 같은 신원을 사용합니다.

ssh -F ssh_config application-a \
  'printf "Access-point identity also applies to root callers\n" | sudo tee /srv/shared/root-caller.txt; stat -c "%u:%g" /srv/shared/root-caller.txt'

소유자는 2001:2001입니다. EFS가 숫자 신원을 적용하려면 2001이나 2002라는 로컬 사용자 계정이 있어야 하는 것은 아닙니다.

A의 마운트는 /app-a만 보여 주며 전체 EFS 루트가 아닙니다. B의 형제 디렉터리는 A 마운트의 하위 디렉터리가 아닙니다.

ssh -F ssh_config application-a 'ls -a /srv/shared'
ssh -F ssh_config application-a 'cat /srv/shared/app-b/private.txt'

마지막 명령은 No such file or directory를 반환하지만 B의 실제 파일은 존재하고 B는 읽을 수 있습니다. 독립된 루트와 다른 내용이 분리를 증명합니다. 로컬 마운트에서 ..로 이동하면 로컬 상위 디렉터리로 가며 EFS 형제 디렉터리로 갈 수 없습니다.

AWS View의 분리된 디렉터리

같은 이름이 서로 다른 루트와 내용을 갖습니다. 식별자는 환경마다 다릅니다.

액세스 포인트와 공유 저장소 삭제하기

이 단계에서는 마운트를 해제하고 자신의 저장소 리소스만 삭제합니다.

마운트 해제 전에 로컬 디렉터리로 복원합니다.

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'

두 액세스 포인트를 삭제하고 남은 포인트가 없는지 확인합니다.

aws efs \
  delete-access-point \
  --access-point-id "$AP_A_ID"
aws efs \
  delete-access-point \
  --access-point-id "$AP_B_ID"
aws efs \
  describe-access-points \
  --file-system-id "$FS_ID"

AccessPoints는 비어 있어야 합니다. 포인트 삭제는 진입점 정의만 지우며 디렉터리의 파일을 삭제하지 않습니다. 여기서는 임시 파일 시스템 전체와 데이터를 삭제합니다.

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==`fs02-application-files`].FileSystemId'
aws ec2 \
  describe-security-groups \
  --filters Name=group-name,Values=fs02-efs-access \
  --query 'SecurityGroups[].GroupId'

두 목록은 비어 있어야 합니다. 준비된 서버, 그룹과 서브넷을 보존하고 단계 검사를 실행하세요.

요약

같은 EFS에서 두 액세스 포인트로 다른 루트와 POSIX 신원을 선택했습니다. 같은 이름에 다른 데이터가 저장되며 root 클라이언트를 포함해 서버가 소유자를 강제했습니다. 이후 마운트를 해제하고 포인트, 연결 지점과 임시 파일 시스템을 제거했습니다.

네트워크 도달성, 마운트 선택과 디렉터리 권한은 다른 계층입니다. 다음 실습에서는 이를 각각 확인해 EFS 연결을 진단합니다.

자세한 내용은 AWS 공식 문서를 참고하세요: 액세스 포인트 루트, 자격 증명 및 권한 범위.