[AWS EFS] 파일들을 마이그레이션 하는 방법.

안녕하세요, 현업 일이 바쁘다 보니
포스팅을 너무 오랜만에 하게 되었습니다.
오늘은 AWS에서 제공하는 파일 시스템을 활용하여
사용중인 파일들을 보다 쉽게 마이그레이션 하는 방법에 대해서 말씀드려보도록 하겠습니다.
감사합니다.

글을 작성하기에 앞서, DataSync 방식은 여러 방법으로 사용이 됩니다
- 데이터센터(on-prem)에서 AWS 환경으로 이관하게 될 때
- 네트워크가 분리된 다른 AWS VPC 환경에서 이관하게 될 때
- 같은 VPC 환경 내부에서 각 다른 n대의 서버로 Sync할 때
저는 3번째 방식에 대해 작성할 예정이며, 여러 방법이 있다곤 말씀드렸지만
DataSync에 방식 차이일 뿐 (설정이 살짝 다름) 동작 원리는 동일하니 참고해서 적용시키면 될 것 같습니다!
01. Amazon EFS 란?
Amazon Elastic File System(EFS)**는 AWS에서 제공하는 완전 관리형 파일 스토리지 서비스 입니다.
📌 주요 특징
- 탄력적 확장: 파일 추가/삭제 시 자동으로 용량 조정
- 고가용성: 여러 가용 영역(AZ)에 자동 복제
- 동시 접근: 수천 개의 EC2 인스턴스가 동시 마운트 가능
- NFS 프로토콜: 표준 파일 시스템 인터페이스 제공
📌 EFS를 사용하는 이유?
제가 생각하였을 때 현업에서 EFS를 사용하는 이유는 아래와 같습니다 (각 환경에 따라 다르다는 말씀 먼저 드립니다)
- 여러 서버 간 파일 공유 (즉, 공유 스토리지로 사용)
- 쇼핑몰/커머스 환경에서의 활용
- 상품 이미지 파일 공유
- 사용자 업로드 파일 저장
- 세션 파일 공유 (여러 웹서버)
- 에디터 첨부 파일 저장
- AutoScaling 환경
- 서버 증설 시 자동으로 파일 접근 가능
- 별도 동기화 작업 불필요.
- 로드 밸런서 뒤 여러 인스턴스 운영 시 필수라고 생각함.
- 비용 효율성
- 사용한 만큼 과금
- 프로비저닝 불필요 (EBS와 다른점)
- Infrequent Access 스토리지 클래스로 비용 절감
EBS와 비교를 한번 해봤습니다.

1-1. 데이터 마이그레이션을 진행한 이유
📌 인프라 환경
- ALB 뒤 여러 EC2 인스턴스
- PHP 언어의 Sympony 프레임 워크
- EFS 용량 : 수 백GB
- 주요 데이터 : 공유 하는 상품 이미지, 업로드 파일, 사용자 로그인 관련 세션
📌 문제 상황
- 기존 EFS가 암호화가 되어있지 않았음.
- 컴플라이언스 요구 사항 : 저장 데이터 암호화 필수
- 서비스 중단 불가.
왜? 암호화가 되어야할까?
- 보안 컴플라이언스 규제
- ISMS-P (정보보호 및 개인정보보호 관리체계)
- PCI-DSS (결제 카드 데이터 보호)
- 개인정보보호법 준수
- 암호화 중요성
- 저장 데이터(Data at Rest) 보호
- 물리적 도난/유출 시에도 데이터 보호
- 감사(Audit) 요구사항 충족
AWS EFS 암호화 특징
저장 데이터 암호화 : AWS KMS 기반 AES-256
전송 데이터 암호화 : TLS 1.2
키 관리 : AWS KMS 자동 관리
1-2. 마이그레이션 계획 수립
✅ 비즈니스 요구 사항
- 서비스 무중단
- 데이터 무손실
- 사용자 세션 유지 (로그인 상태 보존)
- 빠른 전환
✅ 마이그레이션 전략
- 초기 데이터 동기화 (사전 작업)
- 새로 암호화 된 EFS 생성
- 로그인 세션 관련 파일을 제외한 나머지 파일들을 미리 동기화.
- 동기화 중에서 서비스 정상 운영.
- 최종 동기화 (전환 작업 직전)
- 현재 로그인중인 사용자들의 세션 관련 파일들만 동기화
- 소요 시간 : 5분 이내
- 무중단 배포를 위한 순차 전환
- EC2 인스턴스 하나씩 작업
- ALB에서 제거 후, 기존 EFS 신규 EFS로 마운트 변경 후 타겟 그룹에 재등록.
- 재등록 하였을 때 세션 데이터가 맞지 않을 경우를 대비하여 기존에 Stickey Session 설정 완료.
02. EFS를 활용한 데이터 마이그레이션
📌 사전 작업
- KMS 생성 : AWS에서 암호화 Key를 관리해주는 서비스 (Key Management Service)
- EFS 생성 : 위에서 생성한 KMS를 가지고 암호화 된 EFS를 신규 생성.

- Security Group : DataSync가 생성하는 ENI도 같은 보안그룹을 사용하며, 자신에 대한 2049 포트 Allow 필수.

- IAM Policy 생성 : EFS를 마운트 할 EC2 IAM 정책에서 아래 정책 추가 및 수정.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DataSyncOperations",
"Effect": "Allow",
"Action": [
"datasync:StartTaskExecution",
"datasync:DescribeTaskExecution",
"datasync:DescribeTask",
"datasync:ListTaskExecutions"
],
"Resource": "*"
},
{
"Sid": "EC2NetworkOperations",
"Effect": "Allow",
"Action": [
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets"
],
"Resource": "*"
},
{
"Sid": "LoadBalancerOperations",
"Effect": "Allow",
"Action": [
"elasticloadbalancing:DeregisterTargets",
"elasticloadbalancing:RegisterTargets",
"elasticloadbalancing:DescribeTargetHealth",
"elasticloadbalancing:DescribeTargetGroups"
],
"Resource": "*"
}
]
}
- DataSync Task 생성
- 세션 파일을 제외한 파일들을 Sync 하기 위한 Task
- 이때 결과가 실패하였다면, CloudWatch 로그 기록을 확인하여 조치가 가능하도록 구성.

📌 전환 작업
✅ 최종 동기화 진행 (세션 파일 동기화)


이렇게 세션 관련된 파일들만 따로 작업 직전에 빠르게 동기화를 진행하였습니다.
만약에, Mount 교체를 진행할 때 사용자가 로그인 한 세션이 다른 EFS에 저장이 되더라도,
Stickey Session이 설정이 되어 있어서 세션 공유가 되고 있습니다.
또한 저는 일일히 타겟 그룹을 Drain 시키고, 마운트 작업을 수동으로 하기 싫어서,
스크립트를 만들어서 동작 시켰지만 과정을 적어보겠습니다.
✅ 수동 작업 과정 : ALB 타겟 그룹 Draing → EC2 서버 접속 → 신규 EFS로 Mount 교체 → Mount 확인 및 파일 동기화 확인 → ALB 타겟 그룹에 재 등록 → 다른 서버들도 똑같은 방식으로 진행
✅ 각 서버에서 스크립트 실행 (자동화 작업)
#!/bin/bash
set -e
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'
OLD_EFS="보안 상 공유 불가 (EFS ID 입니다)"
NEW_EFS="보안 상 공유 불가 (EFS ID 입니다)"
MOUNT_POINT="/var/www/html/efs"
REGION="ap-northeast-2"
TARGET_GROUP_ARN="arn:aws:elasticloadbalancing:ap-northeast-2:<AWS Account ID>:targetgroup/nkor-pr-nppk-web-apne2-tg01/<타겟그룹 ID>"
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
HOSTNAME=$(hostname)
echo "=========================================="
echo "[$HOSTNAME] EFS 마이그레이션 시작"
echo "Instance ID: $INSTANCE_ID"
echo "시간: $(date '+%Y-%m-%d %H:%M:%S')"
echo "=========================================="
# 1. Target Group에서 제거
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "STEP 1: Target Group에서 제거"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
aws elbv2 deregister-targets \
--target-group-arn $TARGET_GROUP_ARN \
--targets Id=$INSTANCE_ID \
--region $REGION
echo "대기 중 (최대 30초)..."
DRAIN_COUNT=0
MAX_DRAIN_WAIT=3 # 30초 (10초 x 3)
while [ $DRAIN_COUNT -lt $MAX_DRAIN_WAIT ]; do
STATE=$(aws elbv2 describe-target-health \
--target-group-arn $TARGET_GROUP_ARN \
--targets Id=$INSTANCE_ID \
--region $REGION \
--query 'TargetHealthDescriptions[0].TargetHealth.State' \
--output text 2>/dev/null || echo "unused")
if [[ "$STATE" == "unused" ]] || [[ "$STATE" == "" ]] || [[ "$STATE" == "None" ]]; then
echo -e "${GREEN}✓ Target Group에서 제거 완료${NC}"
break
fi
DRAIN_COUNT=$((DRAIN_COUNT + 1))
echo " 상태: $STATE (${DRAIN_COUNT}/${MAX_DRAIN_WAIT})"
if [ $DRAIN_COUNT -eq $MAX_DRAIN_WAIT ]; then
echo -e "${YELLOW}⚠ Draining 타임아웃 - 강제 진행${NC}"
break
fi
sleep 10
done
# 2. EFS 전환
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "STEP 2: EFS 전환"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "현재 마운트:"
mount | grep efs
echo ""
echo "fstab 백업 중..."
cp /etc/fstab /etc/fstab.backup.$(date +%Y%m%d_%H%M%S)
echo -e "${GREEN}✓ 백업 완료${NC}"
echo ""
echo "fstab 수정 중..."
sed -i "s/${OLD_EFS}/${NEW_EFS}/g" /etc/fstab
echo "변경 후:"
grep efs /etc/fstab
echo -e "${GREEN}✓ fstab 수정 완료${NC}"
echo ""
echo "EFS 재마운트 중..."
umount $MOUNT_POINT
mount -a
echo -e "${GREEN}✓ 재마운트 완료${NC}"
echo ""
echo "새 마운트 확인:"
mount | grep efs
NEW_MOUNT=$(mount | grep efs)
if [[ $NEW_MOUNT == *"$NEW_EFS"* ]]; then
echo -e "${GREEN}✓ 새 EFS(암호화) 마운트 성공!${NC}"
else
echo -e "${RED}✗ 마운트 실패!${NC}"
exit 1
fi
# 3. Apache 재시작
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "STEP 3: Apache 재시작"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
apache2ctl graceful
echo -e "${GREEN}✓ Apache 재시작 완료${NC}"
# 4. Target Group에 재등록
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "STEP 4: Target Group에 재등록"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
aws elbv2 register-targets \
--target-group-arn $TARGET_GROUP_ARN \
--targets Id=$INSTANCE_ID \
--region $REGION
echo "대기 중 (Health Check)..."
RETRY_COUNT=0
MAX_RETRIES=30
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
STATE=$(aws elbv2 describe-target-health \
--target-group-arn $TARGET_GROUP_ARN \
--targets Id=$INSTANCE_ID \
--region $REGION \
--query 'TargetHealthDescriptions[0].TargetHealth.State' \
--output text)
if [ "$STATE" == "healthy" ]; then
echo -e "${GREEN}✓ Health Check 통과${NC}"
break
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
echo " 상태: $STATE (${RETRY_COUNT}/${MAX_RETRIES})"
sleep 10
done
echo ""
echo "=========================================="
echo -e "${GREEN}✓ [$HOSTNAME] 마이그레이션 완료!${NC}"
echo "=========================================="
물론 각 서버에서 수동으로 실행시키는 것도 반복 작업이지만
이 또한 Session Manager가 구축이 되어 있다면 Run Command를 활용해서 이 스크립트 명령들을
밀어넣을 수도 있습니다 !
방법은 각 환경에서 다양하게 활용할 수 있다는 점 말씀드립니다.
실제 사전 작업이 잘 이루어 진다면, 본 작업은 세션 파일들만 동기화 하고, 여러 작업들을 자동화로 진행하여
빠르게 진행되었으며 그리고 한번 더! 확인하는 습관 잊지 말도록 합시다!
감사합니다 :)