https://velog.io/@squarebird/AWS-EKS%EC%97%90%EC%84%9C-EFS-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0
이 글은 위의 글을 토대로 쓰였습니다. 사실 내용이 거의 같아서 위의 글만 보셔도 됩니다.
※ S3에 PV를 mount하는 것은 지원되지 않는 듯.
AmazonElasticFileSystemFullAccess이 부여되어 있어야 한다.
(대충 편하게 쓰게 해준다는 뜻)
Kubernetes의 패키지 매니저인 helm으로 aws-efs-csi-driver를 설치한다.
$ curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 > get_helm.sh
$ chmod 700 get_helm.sh
$ ./get_helm.sh
helm을 설치한다.
$ helm repo add aws-efs-csi-driver https://kubernetes-sigs.github.io/aws-efs-csi-driver/
$ helm repo update
helm에 aws-efs-csi-driver repository를 추가한 후 repository를 업데이트해 적용한다.
aws eks update-kubeconfig --region ap-northeast-2 --name <CLUSTER_NAME>
사용할 클러스터에 대한 kubeconfig 파일을 받아온다.
이 작업을 해줘야 aws-efs-csi-driver를 설치할 클러스터가 특정된다.
정확히 말하면, 기본적으로 kubectl등 쿠버네티스 관련 명령어들은 모두 ~/.kube/config 파일을 참조하므로 이 파일을 현재 사용하려는 클러스터의 정보로 업데이트해줘야 하는 것이다.
helm upgrade -i aws-efs-csi-driver aws-efs-csi-driver/aws-efs-csi-driver \
--namespace kube-system \
--set image.repository={{ECR_ENDPOINT}} \
--set controller.serviceAccount.create=false \
--set controller.serviceAccount.name=efs-csi-controller-sa
{{ECR_ENDPOINT}}에는 리전 별 값이 들어간다.
현재 클러스터는 서울 리전에 있으므로 서울 리전에 해당하는 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/aws-efs-csi-driver을 넣으면 된다.
다른 리전의 값은 여기를 참조한다.
vpc_id=$(aws eks describe-cluster \
--name {{EKS_CLUSTER_NAME}} \
--query "cluster.resourcesVpcConfig.vpcId" \
--output text)
echo $vpc_id
현재 클러스터의 이름은 terraform-eks이다.
클러스터의 vpc id 값을 가져온다. 출력(echo $vpc_id)은 제로 가져와졌는지 확인하기 위함이다.
cidr_range=$(aws ec2 describe-vpcs \
--vpc-ids $vpc_id \
--query "Vpcs[].CidrBlock" \
--output text)
echo $cidr_range
앞에서 가져온 vpc id로 해당하는(EKS 클러스터가 쓰고 있는) VPC의 CIDR을 가져온다. 10.0.0.0/16 쓰는 중임
security_group_id=$(aws ec2 create-security-group \
--group-name {{SECURITY_GROUP_NAME}} \
--description "{{SECURITY_GROUP_DESCRIPTION}}" \
--vpc-id $vpc_id \
--output text)
echo $security_group_id
EFS에 적용될 보안 그룹을 생성한다.
aws ec2 authorize-security-group-ingress \
--group-id $security_group_id \
--protocol tcp \
--port 2049 \
--cidr $cidr_range
방금 만든 보안 그룹에 inbound 정책을 추가한다.
이는 EKS 클러스터가 속한 VPC의 CIDR에 대해서만 EFS에 접근할 수 있도록 하는 정책이다.
file_system_id=$(aws efs create-file-system \
--region ap-northeast-2 \
--performance-mode generalPurpose \
--tag Key=Name,Value={{EFS_NAME}} \
--query 'FileSystemId' \
--output text)
EFS를 생성한다(AWS Management Console로 해도 된다).
서울 리전에 efs-for-pv로 생성했다.
vpc_id=$(aws eks describe-cluster \
--name {{EKS_CLUSTER_NAME}} \
--query "cluster.resourcesVpcConfig.vpcId" \
--output text)
EFS를 사용할 EKS 클러스터의 vpc id를 가져온다.
aws ec2 describe-subnets \
--filters "Name=vpc-id,Values=$vpc_id" \
--query 'Subnets[*].{SubnetId: SubnetId,AvailabilityZone: AvailabilityZone,CidrBlock: CidrBlock}' \
--output table
클러스터에 존재하는 subnet 목록을 table 형태로 출력한다.
다음과 같이 출력된다.
-----------------------------------------------------------------
| DescribeSubnets |
+-------------------+--------------+----------------------------+
| AvailabilityZone | CidrBlock | SubnetId |
+-------------------+--------------+----------------------------+
| ap-northeast-2c | 10.0.5.0/24 | subnet-059918778367146f8 |
| ap-northeast-2a | 10.0.1.0/24 | subnet-0884fdec336cbbaba |
| ap-northeast-2a | 10.0.4.0/24 | subnet-09ea85dc0f395e961 |
| ap-northeast-2c | 10.0.2.0/24 | subnet-0734a3ceb1021f249 |
+-------------------+--------------+----------------------------+
서브넷이 4개인데, 이건 클러스터 구성 때문에 그렇다.
가용 영역당 최대 두 개의 노드가 생성될 수 있고, desirable한 노드 수는 1로 설정해놨다.
따라서 각 가용 영역에는 두 개의 서브넷이 생성되고, 실제로는 하나의 worker 노드만 배포되어 있으므로 실제로 쓰이고 있는 서브넷은 두 개 뿐이다.
현재 subnet-0884fdec336cbbaba와 subnet-0734a3ceb1021f249에 worker 노드가 할당되어 있다.
aws efs create-mount-target \
--file-system-id {{EFS_ID}} \
--subnet-id {{SUBNET_ID}} \
--security-groups $security_group_id
mount target을 생성한다.
현재 두 개의 AZ에 서브넷이 존재하므로 두 번 해야 한다.
EFS ID는 aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text로 볼 수 있다.
보안 그룹 ID는 aws ec2 describe-security-groups로 확인할 수 있다.
근데 이건 온갖 내용이 다 떠서 그냥 AWS Management Console에서 보는 게 낫다.
주의할 점은 자격 증명(aws configure)리전을 EFS와 같은 리전으로 해야 한다는 것.
git clone https://github.com/kubernetes-sigs/aws-efs-csi-driver.git
cd aws-efs-csi-driver/examples/kubernetes/multiple_pods/
AWS에서 제공하는 sample로 테스트한다.
aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text
EFS id를 확인한다.
vi aws-efs-csi-driver/examples/kubernetes/multiple_pods/specs/pv.yaml
파일을 연다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: efs-pv
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: efs-sc
csi:
driver: efs.csi.aws.com
volumeHandle: {{FILE_SYSTEM_ID}}
{{FILE_SYSTEM_ID}} 자리에 좀 전에 확인한 EFS id를 넣어준다.
$ cd aws-efs-csi-driver/examples/kubernetes/multiple_pods/specs/
$ kubectl apply -f storageclass.yaml
$ kubectl apply -f pv.yaml
$ kubectl apply -f claim.yaml
$ kubectl apply -f pod1.yaml
$ kubectl apply -f pod2.yaml
테스트를 위한 object들을 배포한다.
pod2의 manifest는 다음과 같다.
apiVersion: v1
kind: Pod
metadata:
name: app2
spec:
containers:
- name: app2
image: busybox
command: ["/bin/sh"]
args: ["-c", "while true; do echo $(date -u) >> /data/out2.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: efs-claim
/data에 persistent volume을 mount하고 /data/out2.txt1를 생성해 5초마다 현재 시각을 쓴다.
pod1의 manifest는 다음과 같다.
apiVersion: v1
kind: Pod
metadata:
name: app1
spec:
containers:
- name: app1
image: busybox
command: ["/bin/sh"]
args: ["-c", "while true; do echo $(date -u) >> /data/out1.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: efs-claim
마찬가지로 /data에 mount하고 /data/out1에 5초마다 현재 시각 정보를 쓴다.
kubectl exec -ti app1 -- tail /data/out2.txt
kubectl exec -ti app2 -- tail /data/out2.txt
따라서 다음과 같은 결과가 나와야 정상이다.

즉 두 pod에서 데이터를 조회할 때 값이 일치해야 한다.
sudo apt-get -y install nfs-common
필요한 패키지를 설치한다.
sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport {{EFS_IP_ADDRESS}}:{{MOUNT_DESTINATION}} {{MOUNT_SOURCE}}
Amazon EFS 페이지에서 [연결] 버튼을 누르면 제공되는 명령어를 쓴다.
{{EFS_IP_ADDRESS}}까지는 제공되고, {{MOUNT_DESTINATION}}과 {{MOUNT_SOURCE}}만 적절히 써주면 된다.