AWS Elastic File System(EFS)은 클라우드 환경에서 다양한 서비스와 인스턴스 간에 데이터를 공유해야 할 때 강력한 솔루션입니다. 이 글에서는 Terraform을 사용하여 EFS를 생성하고, EC2 인스턴스에 마운트하며, 더 나아가 EKS(Elastic Kubernetes Service)와 연동하는 방법을 종합적으로 알아보겠습니다.
AWS EFS 개요
AWS EFS(Elastic File System)는 AWS에서 제공하는 관리형 NFS(Network File System) 서비스입니다. EFS의 주요 특징은 다음과 같습니다:
- 탄력적인 스토리지: 사용한 만큼만 과금되며, 사전에 용량을 지정할 필요가 없습니다.
- 공유 액세스: 여러 EC2 인스턴스, EKS 컨테이너 등에서 동시에 접근할 수 있습니다.
- 가용성: 여러 가용 영역에 걸쳐 데이터를 중복 저장하여 고가용성을 제공합니다.
- 성능 모드: 범용(General Purpose) 또는 최대 I/O 모드 중에서 선택할 수 있습니다.
- 처리량 모드: 버스팅(Bursting) 또는 프로비저닝된(Provisioned) 처리량 모드를 지원합니다.
EFS는 VPC 내에서만 액세스 가능하기 때문에, 로컬 환경에서 직접 접근하려면 VPN 연결이나 AWS Direct Connect를 통해 연결된 상태여야 합니다.
Terraform으로 EFS 생성
Terraform을 사용하면 코드로 EFS 리소스를 생성하고 관리할 수 있습니다. 다음은 EFS를 생성하기 위한 Terraform 코드 예제입니다.
1. EFS 파일 시스템 생성
resource "aws_efs_file_system" "efs" {
creation_token = format(
"%s-%s-%s-efs",
var.company,
var.project,
var.environment
)
tags = {
Name = "dev-dsp-data-efs"
}
lifecycle_policy {
transition_to_ia = "AFTER_30_DAYS"
}
performance_mode = "generalPurpose"
throughput_mode = "bursting"
}
이 코드는 EFS 파일 시스템을 생성합니다. 주요 설정:
creation_token
: EFS 생성을 위한 고유 식별자lifecycle_policy
: 30일 후 비활성 데이터를 IA(Infrequent Access) 스토리지 클래스로 전환performance_mode
: 일반 목적 성능 모드 사용throughput_mode
: 버스팅 모드 사용 (기본 처리량 + 크레딧 기반 추가 처리량)
2. EFS 파일 시스템 정책 설정
resource "aws_efs_file_system_policy" "efs" {
file_system_id = aws_efs_file_system.efs.id
bypass_policy_lockout_safety_check = true
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AccessThroughMountTarget",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": [
"elasticfilesystem:ClientRootAccess",
"elasticfilesystem:ClientWrite",
"elasticfilesystem:ClientMount"
],
"Resource": "${aws_efs_mount_target.efs[0].file_system_arn}",
"Condition": {
"Bool": {
"aws:SecureTransport": "true"
},
"Bool": {
"elasticfilesystem:AccessedViaMountTarget": "true"
}
}
}
]
}
POLICY
}
이 정책은 SSL/TLS를 통해 마운트 타겟을 통해 접근할 때만 EFS에 대한 액세스를 허용합니다.
3. EFS 마운트 타겟 생성
resource "aws_efs_mount_target" "efs" {
count = length(aws_subnet.private)
file_system_id = aws_efs_file_system.efs.id
subnet_id = aws_subnet.private[count.index].id
security_groups = [aws_security_group.efs.id]
}
EFS 마운트 타겟은 각 프라이빗 서브넷에 생성되어 해당 서브넷의 리소스가 EFS에 접근할 수 있게 합니다.
4. EFS 보안 그룹 설정
resource "aws_security_group" "efs" {
name = format(
"%s-%s-%s-efs-sg",
var.company,
var.project,
var.environment
)
description = "Allow EFS inbound traffic from VPC"
vpc_id = aws_vpc.vpc_main.id
ingress {
description = "NFS traffic from VPC"
from_port = 2049
to_port = 2049
protocol = "tcp"
cidr_blocks = [var.vpc_cidr]
}
tags = {
Name = format(
"%s-%s-%s-efs-sg",
var.company,
var.project,
var.environment
)
}
}
이 보안 그룹은 VPC 내에서 NFS 포트(2049)를 통한 트래픽만 허용하여 EFS에 대한 액세스를 제한합니다.
EC2 인스턴스에 EFS 마운트
Bastion EC2 인스턴스 또는 기타 EC2 인스턴스에 EFS를 마운트하는 방법에는 여러 가지가 있습니다.
1. 수동 마운트 방법
amazon-efs-utils 사용 (권장)
# amazon-efs-utils 패키지 설치
sudo yum install -y amazon-efs-utils
# TLS를 사용하여 마운트
sudo mkdir -p /data
sudo mount -t efs -o tls fs-000e53414b88c22aa:/ /data
표준 NFS 클라이언트 사용
sudo mkdir -p /data
sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport fs-013f1a37af152013f.efs.ap-northeast-2.amazonaws.com:/ /data
2. 부팅 시 자동 마운트 설정
시스템 재부팅 후에도 EFS가 자동으로 마운트되도록 /etc/fstab
파일에 다음 내용을 추가합니다:
fs-09b0a758282d556c1.efs.ap-northeast-2.amazonaws.com:/ /home/ec2-user/data nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,_netdev 0 0
_netdev
옵션은 네트워크가 활성화된 후에 파일 시스템을 마운트하도록 지정합니다.
설정 후 다음 명령어로 테스트할 수 있습니다:
sudo mount -fav
3. EC2 User Data를 통한 자동 마운트
Terraform에서 EC2 인스턴스를 생성할 때 user_data
속성을 사용하여 인스턴스 시작 시 자동으로 EFS를 마운트하도록 설정할 수 있습니다:
resource "aws_instance" "bastion" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
# 다른 설정들...
user_data = <<-EOF
#!/bin/bash
sudo yum install -y amazon-efs-utils
sudo mkdir -p /data
sudo mount -t efs -o tls ${aws_efs_file_system.efs.id}:/ /data
# 자동 마운트를 위한 fstab 설정
echo "${aws_efs_file_system.efs.id}:/ /data efs defaults,tls 0 0" | sudo tee -a /etc/fstab
EOF
}
이 접근 방식은 인스턴스가 처음 시작될 때 EFS를 마운트하고, /etc/fstab
에 항목을 추가하여 재부팅 후에도 자동으로 마운트되도록 합니다.
AWS EKS와 EFS 연동
EKS 클러스터에서 EFS를 사용하려면 EFS CSI(Container Storage Interface) 드라이버를 설치하고 구성해야 합니다. 단, Fargate 노드를 사용하는 경우에는 CSI 드라이버가 자동으로 탑재되므로 별도 설치가 필요 없습니다.
1. 필요한 IAM 정책 및 역할 생성
# CSI 드라이버에 필요한 IAM 정책 파일 다운로드
curl -o iam-policy-example.json https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/master/docs/iam-policy-example.json
# IAM 정책 생성
aws iam create-policy \
--policy-name AmazonEKS_EFS_CSI_Driver_Policy \
--policy-document file://iam-policy-example.json
# EKS 클러스터에 서비스 계정 생성 및 정책 연결
eksctl create iamserviceaccount \
--cluster dev-dsp \
--namespace kube-system \
--name efs-csi-controller-sa \
--attach-policy-arn arn:aws:iam::534056516338:policy/AmazonEKS_EFS_CSI_Driver_Policy \
--approve \
--region ap-northeast-2
2. Helm을 사용하여 EFS CSI 드라이버 설치
# Helm 저장소 추가
helm repo add aws-efs-csi-driver https://kubernetes-sigs.github.io/aws-efs-csi-driver/
# 저장소 업데이트
helm repo update
# EFS CSI 드라이버 설치
helm upgrade -i aws-efs-csi-driver aws-efs-csi-driver/aws-efs-csi-driver \
--namespace kube-system \
--set image.repository=602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/aws-efs-csi-driver \
--set controller.serviceAccount.create=false \
--set controller.serviceAccount.name=efs-csi-controller-sa
참고: 배포하는 리전에 맞는
image.repository
값을 사용해야 합니다. AWS 공식 문서에서 각 리전별 이미지 URI를 확인할 수 있습니다: https://docs.aws.amazon.com/eks/latest/userguide/add-ons-images.html
3. StorageClass, PV, PVC 생성
EKS에서 EFS를 사용하기 위해 Kubernetes 리소스를 생성해야 합니다. Helm 템플릿을 사용하여 이러한 리소스를 관리할 수 있습니다.
StorageClass 정의
# StorageClass.yaml
{{- range $storageClass := .Values.storageClasses }}
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: {{ $storageClass.name }}
provisioner: {{ $storageClass.provisioner }}
reclaimPolicy: {{ $storageClass.reclaimPolicy }}
---
{{- end }}
PersistentVolume 정의
# PersistentVolume.yaml
{{- range $persistentVolume := .Values.persistentVolumes }}
apiVersion: v1
kind: PersistentVolume
metadata:
name: {{ $persistentVolume.name }}
spec:
capacity:
storage: {{ $persistentVolume.storage }}
volumeMode: {{ $persistentVolume.volumeMode }}
{{ with $persistentVolume.accessModes }}
accessModes:
{{- toYaml . | nindent 8 }}
{{ end }}
persistentVolumeReclaimPolicy: {{ $persistentVolume.reclaimPolicy }}
storageClassName: {{ $persistentVolume.storageClassName }}
csi:
driver: {{ $persistentVolume.driver }}
volumeHandle: {{ $persistentVolume.volumeHandle }}
---
{{- end }}
PersistentVolumeClaim 정의
# PersistentVolumeClaim.yaml
{{- range $persistentVolumeClaim := .Values.persistentVolumeClaims }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ $persistentVolumeClaim.name }}
spec:
{{ with $persistentVolumeClaim.accessModes }}
accessModes:
{{- toYaml . | nindent 8 }}
{{ end }}
storageClassName: {{ $persistentVolumeClaim.storageClassName }}
resources:
requests:
storage: {{ $persistentVolumeClaim.storage }}
---
{{- end }}
4. Deployment에 볼륨 마운트 설정
spec:
# ... 다른 설정들 ...
containers:
- name: {{ .Chart.Name }}
# ... 다른 설정들 ...
volumeMounts:
{{- range $persistentVolumeClaim := .Values.persistentVolumeClaims }}
- name: {{ $persistentVolumeClaim.volumeName }}
mountPath: {{ $persistentVolumeClaim.mountPath }}
{{- end}}
volumes:
{{- range $persistentVolumeClaim := .Values.persistentVolumeClaims }}
- name: {{ $persistentVolumeClaim.volumeName }}
persistentVolumeClaim:
claimName: {{ $persistentVolumeClaim.name }}
{{- end}}
EFS의 중요한 특징 (EKS 사용 시 고려사항)
- 동적 할당: EFS는 할당량을 지원하지 않습니다. 미리 용량을 지정하지 않고, 실제 사용하는 만큼만 과금됩니다.
- Fargate와의 연동: Fargate 노드에서는 동적 PV 프로비저닝을 사용할 수 없고, 정적 프로비저닝만 가능합니다.
- 다중 AZ 지원: EFS는 여러 가용 영역에 걸쳐 데이터를 저장하므로, 다양한 노드에서 동일한 데이터에 접근할 수 있습니다.
데이터 전송 자동화
AWS EFS와 다른 스토리지 서비스(예: S3) 간에 데이터를 자동으로 전송하기 위해 AWS DataSync를 사용할 수 있습니다. GitHub Actions와 AWS CLI를 함께 사용하여 파이프라인을 구축할 수 있습니다.
DataSync Task를 트리거하기 위한 IAM 정책
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"datasync:CreateLocationS3",
"datasync:CreateTask",
"datasync:ListAgents",
"datasync:ListLocations",
"datasync:ListTaskExecutions",
"datasync:CreateLocationEfs",
"datasync:ListTasks",
"datasync:StartTaskExecution"
// 기타 필요한 권한...
],
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "datasync:*",
"Resource": "arn:aws:datasync:ap-northeast-2:534056516338:task/task-01952c9a18*"
}
]
}
또한 DataSync가 네트워크 인터페이스를 생성하고 관리할 수 있도록 EC2 관련 권한도 필요합니다:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:CreateNetworkInterface",
"ec2:DescribeInstances",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface",
"ec2:AttachNetworkInterface"
],
"Resource": "*"
}
]
}
GitHub Actions 워크플로우를 사용하여 이러한 정책을 가진 IAM 계정으로 AWS DataSync 작업을 자동으로 트리거할 수 있습니다.
마무리
AWS EFS는 다양한 AWS 서비스와 쉽게 통합할 수 있는 관리형 파일 시스템 서비스입니다. Terraform을 사용하여 인프라를 코드로 관리하고, EC2 인스턴스와 EKS 클러스터에 EFS를 마운트하여 영구적이고 공유 가능한 스토리지 솔루션을 구현할 수 있습니다.
이 글에서 다룬 내용을 활용하면 클라우드 환경에서 더 효율적인 데이터 공유 및 저장 전략을 구축할 수 있을 것입니다. 각 조직의 특정 요구 사항에 맞게 설정을 조정하고, AWS의 다른 서비스와 통합하여 더욱 강력한 솔루션을 만들어보세요.
참고 자료: