일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- MFA 분실
- Mac Terraform
- Authenticator
- Terrafrom
- 볼륨 연결
- 리눅스
- EC2
- EBS
- docker 상태
- 컨테이너 터미널 로그아웃
- AWS
- 테라폼 맥
- 컨테이너 터미널
- 테라폼 설치
- AWS EBS
- ebs 마운트
- epxress-generator
- /etc/fstab 설정
- EBS 최적화
- 테라폼 캐시
- 디스크 성능테스트
- docker -i -t
- MFA 인증
- 테라폼 자동완성
- ebs 재부팅
- 볼륨추가
- 리눅스 시간대
- /etc/fstab 뜻
- xfs_quota
- 텔레메트리란
- Today
- Total
I got IT
EKS 스토리지 - Stateful 애플리케이션 구성(with PV) 본문
※ 해당 글은 CloudNet@ gasida 님의 EKS 스터디 내용을 참고하여 작성하였습니다.
이번 포스팅에서는 쿠버네티스 스토리지 개념과 EKS 환경에서 쿠버네티스 스토리지가 어떻게 작동하는 지에 대해서 말씀 드리도록 하겠습니다.
실습환경 구성하기
지난 포스팅들과 마찬가지로 실습환경을 먼저 구성해 줍니다.
실습 환경 아키텍처
- 2개의 VPC: EKS 배포용(myeks-vpc)과 운영용(operator-vpc)으로 구분
- myeks-vpc 에 각기 AZ를 사용하는 퍼블릭/프라이빗 서브넷 배치
- 로드밸런서 배포를 위한 퍼블릭/프라이빗 서브넷에 태그 설정
- EFS 추가: myeks-vpc의 Public 서브넷에 배포
- VPC Peering 설정: 내부 통신을 위한 VPC 연결 구성
- Node Group 배포: EKS 클러스터 내 관리형 노드 그룹 생성
실습 환경 배포
# CloudFormation YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/myeks-3week.yaml
# CloudFormation 스택 배포
aws cloudformation deploy --template-file myeks-3week.yaml \
--stack-name myeks \
--parameter-overrides KeyName=<your-key> SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 \
--region ap-northeast-2
# 배포된 운영서버 EC2 IP 출력
aws cloudformation describe-stacks --stack-name myeks \
--query 'Stacks[*].Outputs[*].OutputValue' --output text
# SSH로 운영서버 접속
ssh -i <ssh 키파일> ec2-user@$(aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text)
eksctl을 활용한 EKS 클러스터 배포
환경 변수 설정
export CLUSTER_NAME=myeks
# VPC 및 서브넷 정보 확인
export VPCID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" --query 'Vpcs[*].VpcId' --output text)
export PubSubnet1=$(aws ec2 describe-subnets --filters "Name=tag:Name,Values=$CLUSTER_NAME-Vpc1PublicSubnet1" --query "Subnets[0].[SubnetId]" --output text)
export PubSubnet2=$(aws ec2 describe-subnets --filters "Name=tag:Name,Values=$CLUSTER_NAME-Vpc1PublicSubnet2" --query "Subnets[0].[SubnetId]" --output text)
export PubSubnet3=$(aws ec2 describe-subnets --filters "Name=tag:Name,Values=$CLUSTER_NAME-Vpc1PublicSubnet3" --query "Subnets[0].[SubnetId]" --output text)
EKS 클러스터 YAML 파일 작성
cat << EOF > myeks.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: myeks
region: ap-northeast-2
version: "1.31"
iam:
withOIDC: true # enables the IAM OIDC provider as well as IRSA for the Amazon CNI plugin
serviceAccounts: # service accounts to create in the cluster. See IAM Service Accounts
- metadata:
name: aws-load-balancer-controller
namespace: kube-system
wellKnownPolicies:
awsLoadBalancerController: true
vpc:
cidr: 192.168.0.0/16
clusterEndpoints:
privateAccess: true # if you only want to allow private access to the cluster
publicAccess: true # if you want to allow public access to the cluster
id: $VPCID
subnets:
public:
ap-northeast-2a:
az: ap-northeast-2a
cidr: 192.168.1.0/24
id: $PubSubnet1
ap-northeast-2b:
az: ap-northeast-2b
cidr: 192.168.2.0/24
id: $PubSubnet2
ap-northeast-2c:
az: ap-northeast-2c
cidr: 192.168.3.0/24
id: $PubSubnet3
addons:
- name: vpc-cni # no version is specified so it deploys the default version
version: latest # auto discovers the latest available
attachPolicyARNs: # attach IAM policies to the add-on's service account
- arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
configurationValues: |-
enableNetworkPolicy: "true"
- name: kube-proxy
version: latest
- name: coredns
version: latest
- name: metrics-server
version: latest
managedNodeGroups:
- amiFamily: AmazonLinux2023
desiredCapacity: 3
iam:
withAddonPolicies:
certManager: true # Enable cert-manager
externalDNS: true # Enable ExternalDNS
instanceType: t3.medium
preBootstrapCommands:
# install additional packages
- "dnf install nvme-cli links tree tcpdump sysstat ipvsadm ipset bind-utils htop -y"
labels:
alpha.eksctl.io/cluster-name: myeks
alpha.eksctl.io/nodegroup-name: ng1
maxPodsPerNode: 100 # t3.medium 기본 설정인 17개보다 많은 Pod 배포 가능
maxSize: 3
minSize: 3
name: ng1
ssh:
allow: true # 이 설정을 활성화하면 클러스터 생성 시 자동으로 원격 접속이 가능한 보안 그룹이 생성됨
publicKeyName: $SSHKEYNAME
tags:
alpha.eksctl.io/nodegroup-name: ng1
alpha.eksctl.io/nodegroup-type: managed
volumeIOPS: 3000
volumeSize: 120
volumeThroughput: 125
volumeType: gp3
EOF
해당 yaml 파일은 기본적으로 다음과 같은 구성요소를 포함합니다.
- IAM 및 OIDC: 클러스터에서 OIDC를 활성화하여 IAM 역할을 파드에 부여할 수 있으며, aws-load-balancer-controller에 필요한 IAM 정책을 사전에 설정합니다.
- 네트워킹:전용 VPC (CIDR 192.168.0.0/16)를 사용하며, 클러스터 엔드포인트는 private와 public 모두 접근 가능하도록 설정합니다.세 개의 public 서브넷이 각기 다른 AZ(ap-northeast-2a, 2b, 2c)에 배치되어 있습니다.
- 애드온(Addons): vpc-cni (네트워크 정책 활성화 포함), kube-proxy, coredns, metrics-server 등이 설치되어 클러스터 운영에 필요한 기본 서비스들을 제공합니다.
- Managed Node Group:AmazonLinux2023 기반의 t3.medium 인스턴스 3대를 사용합니다.
노드 당 최대 100개의 파드를 지원하도록 설정되어 있으며, certManager 및 externalDNS 등의 추가 정책이 적용됩니다.
클러스터 생성하기
eksctl create cluster -f myeks.yaml --verbose 4
※ 클러스터 생성 확인을 위해 verbose 옵션을 항상 켜줍니다. 4권장
관리형 노드 그룹 접속 및 정보 확인
EC2 인스턴스 정보 확인 및 SSH 접속
# 인스턴스 정보 확인 1: 전체 노드 목록 조회
aws ec2 describe-instances --query "Reservations[*].Instances[*].{InstanceID:InstanceId, PublicIPAdd:PublicIpAddress, PrivateIPAdd:PrivateIpAddress, InstanceName:Tags[?Key=='Name']|[0].Value, Status:State.Name}" --filters Name=instance-state-name,Values=running --output table
# EC2 인스턴스 정보 확인: AZ, ID, 공인IP 출력
aws ec2 describe-instances \
--filters "Name=tag:Name,Values=myeks-ng1-Node" \
--query "Reservations[].Instances[].{InstanceID:InstanceId, PublicIP:PublicIpAddress, AZ:Placement.AvailabilityZone}" \
--output table
# AZ별 공인 IP 확인
# AZ1 배치된 EC2 공인 IP
aws ec2 describe-instances \
--filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2a" \
--query 'Reservations[*].Instances[*].PublicIpAddress' \
--output text
# AZ2 배치된 EC2 공인 IP
aws ec2 describe-instances \
--filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2b" \
--query 'Reservations[*].Instances[*].PublicIpAddress' \
--output text
# AZ3 배치된 EC2 공인 IP
aws ec2 describe-instances \
--filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2c" \
--query 'Reservations[*].Instances[*].PublicIpAddress' \
--output text
# 공인 IP를 변수로 저장
export N1=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2a" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
export N2=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2b" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
export N3=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2c" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
echo $N1, $N2, $N3
보안 그룹 인바운드 규칙 추가
SSH 접속을 위해 로컬환경의 IP 및 운영 서버 IP를 보안 그룹 인바운드 규칙에 추가해 줍니다.
# 'remoteAccess' 포함된 관리형 노드 그룹의 보안 그룹 ID 확인
aws ec2 describe-security-groups --filters "Name=group-name,Values=*remoteAccess*" | jq
export MNSGID=$(aws ec2 describe-security-groups --filters "Name=group-name,Values=*remoteAccess*" --query 'SecurityGroups[*].GroupId' --output text)
# 해당 보안그룹 inbound에 집 공인 IP 허용
aws ec2 authorize-security-group-ingress --group-id $MNSGID --protocol '-1' --cidr $(curl -s ipinfo.io/ip)/32
# 해당 보안그룹 inbound에 운영 서버 내부 IP 허용
aws ec2 authorize-security-group-ingress --group-id $MNSGID --protocol '-1' --cidr 172.20.1.100/32
# ping 테스트
ping -c 2 $N1
ping -c 2 $N2
ping -c 2 $N3
# SSH 접속 확인
ssh -i <SSH 키> -o StrictHostKeyChecking=no ec2-user@$N1 hostname
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -o StrictHostKeyChecking=no ec2-user@$i hostname; echo; done
ssh ec2-user@$N1
exit
ssh ec2-user@$N2
exit
ssh ec2-user@$N2
exit
이 과정을 통해 원격으로 관리되는 노드 그룹에 대한 보안 그룹 설정과 네트워크/SSH 연결이 올바르게 구성되었는지 검증할 수 있습니다.
각 워커노드에 ping 테스트를 하여 통신을 확인합니다.
워커노드 ssh접속 확인 및 hostname 출력
노드 정보 확인, max-pods 확인
# 노드의 기본 정보 출력
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i hostnamectl; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c addr; echo; done
# 스토리지 정보 확인
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i lsblk; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i df -hT /; echo; done
# 스토리지 클래스 및 CSI 노드 정보 확인
kubectl get sc
kubectl describe sc gp2
kubectl get crd
kubectl get csinodes
각 노드에 접속하여 스토리지 구성을 확인합니다.
max-pods 정보 확인
# 노드별 max-pods 값 확인
kubectl describe node | grep Capacity: -A13
kubectl get nodes -o custom-columns="NAME:.metadata.name,MAXPODS:.status.capacity.pods"
# 노드에서 확인
ssh ec2-user@$N1 sudo cat /etc/kubernetes/kubelet/config.json | jq
# 각 노드에서 maxPods 기본 값 확인 (17)
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo cat /etc/kubernetes/kubelet/config.json | grep maxPods; echo; done
# 사용자 지정 maxPods 값 확인 (100) (config.json.d/00-nodeadm.conf에서 오버레이 적용)
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo cat /etc/kubernetes/kubelet/config.json.d/00-nodeadm.conf | grep maxPods; echo; done
describe node 명령어 후 Capacity 부분에서 pods 속성에서 살펴볼 수 있습니다.
EKS kubeconfig 설정 및 EFS 마운트 테스트
# IAM 자격 증명 설정
aws configure
# get-caller-identity로 현재 IAM 사용자 확인
aws sts get-caller-identity --query Arn
# kubeconfig 생성 및 적용
aws eks update-kubeconfig --name myeks --user-alias admin
# 클러스터 정보 확인
kubectl cluster-info
kubectl ns default
kubectl get node -v6
원활한 테스트를 위해 Admin 권한 User를 설정해줍니다.
EFS 마운트 테스트
운영 서버가 VPC 피어링을 활용해 EFS를 원격으로 마운트하고 해당 스토리지를 제대로 사용할 수 있는지 확인합니다. 이 과정을 통해 서버가 EFS에 접근하여 데이터를 저장 및 조회하는 기능이 정상 동작하는지 점검합니다.
# 현재 EFS 정보 확인
aws efs describe-file-systems | jq
# 파일 시스템 ID만 출력
aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text
# EFS 마운트 타겟 확인
aws efs describe-mount-targets --file-system-id $(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text) | jq
# EFS 마운트 타겟 확인 (IP 주소만 출력)
aws efs describe-mount-targets --file-system-id $(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text) --query "MountTargets[*].IpAddress" --output text

각 서브넷 대역의 랜덤 아이피로 하나씩 efs 마운트 타겟이 생성됩니다. 이 마운트 타겟은 클라이언트가 efs 와 연결할 수 있는 엔드포인트가 됩니다.
EFS 도메인 확인 (DNS 질의 테스트)
Amazon EFS는 기본적으로 내부 네트워크의 프라이빗 DNS를 통해 접근할 수 있으며, VPC 내에서는 fs-xxxxxx.efs.ap-northeast-2.amazonaws.com과 같은 도메인 이름을 사용해 연결됩니다. 그러나 운영 서버가 동일한 VPC에 위치하지 않을 경우, DNS 이름 확인 과정에서 문제가 발생할 수 있습니다.
dig +short $(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text).efs.ap-northeast-2.amazonaws.com
이 때 해결방법은 다음과 같습니다.
- DNS 수동 설정: EFS 마운트 타겟의 IP 주소를 직접 사용
- AWS PrivateLink 활용: EFS에 PrivateLink를 설정하여 Cross-VPC 접근 허용
- Route 53 Private Hosted Zone 공유: 동일한 Private DNS 설정을 활용
EFS 마운트 테스트 (NFS 기반 마운트)
EFS는 NFS(Network File System) 프로토콜을 사용하여 EC2에서 공유 스토리지로 마운트할 수 있습니다.
# EFS 마운트
EFSIP1=<EFS 마운트 타겟 중 하나>
EFSIP1=192.168.1.140
# 현재 마운트된 파일 시스템 확인
df -hT
# 마운트할 디렉터리 생성
mkdir /mnt/myefs
# EFS 마운트 실행
mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport $EFSIP1:/ /mnt/myefs
# 마운트 확인
findmnt -t nfs4
df -hT --type nfs4
이때 aws 가이드를 살펴보면 타겟에 efs dns 를 입력하기도 하지만 실제 타겟 eni ip를 통해 마운트할 수 있습니다. 현재는 VPC Peering 구성이므로 Ip 를 통한 마운트가 필요합니다.
EFS 에 데이터 저장
EFS가 정상적으로 마운트되었는지 테스트하기 위해 파일을 생성하고 저장된 데이터를 확인합니다.
# NFS 요청 통계 확인
nfsstat
# EFS에 데이터 저장
echo "EKS Workshop" > /mnt/myefs/memo.txt
# 다시 NFS 요청 통계 확인 (NFS를 통해 요청 발생)
nfsstat
# 저장된 파일 확인
ls -l /mnt/myefs
cat /mnt/myefs/memo.txt
nfsstat 명령어 조회시 액세스 기록 및 사용량 등에 대한 정보가 바뀐걸 확인할 수 있습니다.
운영 서버에서 EFS를 영구 마운트하기
운영 환경에서는 서버가 재부팅된 이후에도 EFS가 자동으로 마운트되도록 설정해야 합니다. 이를 위해 /etc/fstab 파일을 수정하여 EFS를 영구적으로 마운트합니다.
# 기존 fstab 백업
sudo cp /etc/fstab /etc/fstab.backup
# EFS 영구 마운트 설정 추가
echo "$EFSIP1:/ /mnt/myefs nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport 0 0" | sudo tee -a /etc/fstab
# /etc/fstab 파일 내용 확인
cat /etc/fstab
VPC Peering 환경에서는 efs.ap-northeast-2.amazonaws.com 같은 DNS 주소 대신 IP 주소를 사용해야 합니다. ❗️❗️❗️
AWS LoadBalancerController, ExternalDNS, kube-ops-view 설치
EKS에서 ALB Ingress, 자동 DNS 등록, Kubernetes 리소스 모니터링 도구를 설치하고 설정합니다.
# kube-ops-view 설치
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=ClusterIP --set env.TZ="Asia/Seoul" --namespace kube-system
# AWS LoadBalancerController 설치
helm repo add eks https://aws.github.io/eks-charts
helm repo update
kubectl get sa -n kube-system aws-load-balancer-controller
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=$CLUSTER_NAME \
--set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller
# ExternalDNS 설치
MyDomain=<your domain>
MyDomain=josh-eks.com
MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "$MyDomain." --query "HostedZones[0].Id" --output text)
curl -s https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml | MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst | kubectl apply -f -
HTTPS 통신을 위한 ALB Ingress 설정
AWS Certificate Manager(ACM)을 사용하여 HTTPS 트래픽을 허용하도록 ALB Ingress를 설정합니다.
먼저, 해당 리전에 발급된 SSL/TLS 인증서가 있는지 확인하고 arn을 가져옵니다.
# 사용 리전의 인증서 ARN 확인
CERT_ARN=$(aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text)
echo $CERT_ARN
kube-ops-view Ingress 설정
Ingress 그룹 기능을 활용하면 여러 개의 Ingress 리소스가 하나의 ALB를 함께 사용할 수 있도록 구성할 수 있습니다.
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
alb.ingress.kubernetes.io/group.name: study
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/ssl-redirect: "443"
alb.ingress.kubernetes.io/success-codes: 200-399
alb.ingress.kubernetes.io/target-type: ip
labels:
app.kubernetes.io/name: kubeopsview
name: kubeopsview
namespace: kube-system
spec:
ingressClassName: alb
rules:
- host: kubeopsview.$MyDomain
http:
paths:
- backend:
service:
name: kube-ops-view
port:
number: 8080
path: /
pathType: Prefix
EOF
설치된 리소스가 정상적으로 동작하는지 확인합니다.
# 설치된 파드 확인
kubectl get pods -n kube-system
# 서비스, 엔드포인트, Ingress 확인
kubectl get ingress,svc,ep -n kube-system
# Kube Ops View URL 출력
echo -e "Kube Ops View URL = https://kubeopsview.$MyDomain/#scale=1.5"
# macOS에서는 자동으로 브라우저 열기
open "https://kubeopsview.$MyDomain/#scale=1.5"
kubeopsview.$MyDomain 도메인을 통해 HTTPS 프로토콜로 Kubernetes 리소스 모니터링 대시보드에 접근할 수 있습니다.
K8S 스토리지 개념
K8S의 스토리지 사상은 크게 다음과 같이 3 종류로 나뉘어 집니다.
emptyDir
▪️ 파드(Pod)가 생성될 때 빈 디렉토리를 마운트하여 사용하는 임시 스토리지입니다.
▪️ 파드가 실행되는 동안에는 데이터가 유지되지만, 파드가 삭제되거나 재시작되면 해당 디렉토리에 있던 데이터는 모두 사라집니다.
▪️ 노드 로컬 스토리지를 사용하므로, 파드가 다른 노드로 이동하면(emptyDir 볼륨 포함) 데이터를 유지할 수 없습니다.
따라서 파드의 라이프사이클이 종료되면 파드에서 생성된 데이터는 모두 휘발되게 됩니다. 이는 용도에 따라서 장점으로도 활용할 수 있습니다. emptypDir 스토리지를 사용하는 경우는 다음과 같은 경우가 있을 수 있습니다.
▪️ 캐시(Caching)나 임시 파일 저장: 웹 서버나 애플리케이션에서 일시적으로 생성되는 대용량 임시 파일을 저장할 때.
▪️ 로그 버퍼: 어플리케이션 로그가 외부 로그 스토리지(예: CloudWatch, ELK 등)로 전송되기 전, 잠시 저장해두는 용도.
▪️ 빌드 혹은 임시 컴파일 작업: CI/CD 파이프라인에서, 소스 코드를 빌드할 때 생성되는 임시 산출물을 저장.
hostPath
▪️ 호스트 노드(Worker Node)의 특정 디렉토리나 파일을 파드 내부로 마운트해 사용하는 방식입니다.
▪️ 노드의 파일 시스템 경로를 직접 활용하므로, 노드 환경에 의존적이며, 이 또한 emptyDir 처럼 파드가 다른 노드로 이동하면 동일한 경로나 데이터를 사용할 수 없습니다.
▪️ 권한 설정, 보안 이슈 등을 세심하게 고려해야 합니다. 호스트 노드의 파일 시스템을 직접 쓰기 때문에 보안적 리스크가 클 수 있습니다. 만약 컨테이너의 Root 계정이 탈취될 경우 호스트의 Root 권한까지 탈취될 수 있습니다. ❗️❗️❗️
hostPath는 노드의 데이터에 접근할 수 있기 때문에 다음과 같은 유즈케이스에 사용될 수 있습니다.
▪️호스트 시스템과의 데이터 공유: 노드에 설치된 외부 툴이나 구성 파일에 직접 접근해야 하는 특수 상황.
▪️GPU 드라이버, 디바이스 매핑: GPU 자원을 파드에서 활용하기 위해 호스트에 설치된 드라이버를 마운트할 때.
▪️로깅 / 모니터링 에이전트: 노드에서 수집된 시스템 로그, 메트릭 파일 등을 직접 파드 내에서 읽어갈 때.
PersistentVolume (PV) / PersistentVolumeClaim (PVC)
▪️쿠버네티스에서 영구 스토리지를 제공하기 위한 공식적인 메커니즘입니다.
▪️PersistentVolume(PV): 클러스터 안에서 실제 스토리지를 나타내는 객체이며, 물리적으로는 EBS, NFS, GCE Persistent Disk 등 다양한 외부 스토리지와 연결될 수 있습니다.
▪️PersistentVolumeClaim(PVC): 파드(혹은 사용자가) 필요로 하는 스토리지 용량, 접근 모드 등을 선언하면, 쿠버네티스가 이를 만족하는 PV와 바인딩(Binding)합니다.
▪️파드가 삭제되더라도 PVC를 해제/삭제하지 않는 한, 데이터는 PV에 계속 남아 재연결이 가능하므로 데이터 영속성을 보장합니다.
즉, 파드의 라이프사이클이 종료되더라도 영구적으로 보존이 필요한 경우에 사용이 필요합니다. 하지만 스토리지 사용량 관리를 안하면 disk full 등의 위험포인트 들이 있습니다. 운영 환경에서는 PV를 사용하고 주기적으로 데이터를 삭제 및 아카이빙 하는 것이 일반적입니다. PV는 다음과 같이 다양한 stateful 애플리케이션을 구현하는데 필요합니다.
▪️데이터베이스 (MySQL, PostgreSQL, MongoDB 등): DB가 재시작되더라도 기존 데이터를 유지해야 할 때.
▪️메시지 큐 및 브로커 (Kafka, RabbitMQ 등): 장애나 업데이트 중에도 메시지 로그나 큐 데이터를 영구적으로 보존해야 함.
▪️웹 애플리케이션에서의 업로드 파일: 사용자 이미지, 첨부파일 등의 컨텐츠를 영구 스토리지에 저장하고, 파드 교체 시에도 데이터가 유지되어야 하는 경우.
emptyDir, hostPath 실습해 보기
간단한 레디스 파드를 올려서 파드에서 발생한 데이터가 어떻게 보관 되는지 살펴보도록 합니다.
emptyDir 스토리지 동작 확인
# 모니터링
kubectl get pod -w
# redis 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
terminationGracePeriodSeconds: 0
containers:
- name: redis
image: redis
volumeMounts:
- name: redis-storage
mountPath: /data/redis
volumes:
- name: redis-storage
emptyDir: {}
EOF
# redis 파드 내에 파일 작성
kubectl exec -it redis -- pwd
kubectl exec -it redis -- sh -c "echo hello > /data/hello.txt"
kubectl exec -it redis -- cat /data/hello.txt
# ps 설치
kubectl exec -it redis -- sh -c "apt update && apt install procps -y"
kubectl exec -it redis -- ps aux
# redis 프로세스 강제 종료 : 파드가 어떻게 되나요? hint) restartPolicy
kubectl exec -it redis -- kill 1
kubectl get pod
# redis 파드 내에 파일 확인
kubectl exec -it redis -- cat /data/hello.txt
kubectl exec -it redis -- ls -l /data
# 파드 삭제
kubectl delete pod redis
redis 프로세스를 강제로 종료하게 되면 컨테이너가 종료됩니다. 이 때 컨테이너는 종료되도 Pod는 살아있기 때문에 데이터는 유지됩니다.
하지만 emptyDir 볼륨은 파드가 삭제되면 데이터가 사라집니다. 새로 동일한 파드를 다시 생성해서 조회하면 /data/hello.txt는 없습니다.
hostPath 스토리지 동작 확인
Kubernetes에서는 특정 노드에 위치한 로컬 디렉토리를 볼륨으로 사용할 수 있도록 local-path-provisioner를 활용할 수 있습니다. 이 방식은 스토리지가 물리적으로 특정 노드에 묶여 있는 상황에서 유용하며, 해당 노드에서만 접근 가능한 로컬 스토리지를 효율적으로 관리할 수 있도록 돕습니다. local-path-provisioner 공식 문서: GitHub 링크
hostPath VS Local Path Provisioner
- hostPath는 간단하게 로컬 디렉토리를 마운트할 수 있으나, 노드에 종속되어 이식성과 보안 측면에서 한계가 있고, 동적 관리 기능이 없습니다.
- Local Path Provisioner는 StorageClass와 연계해 로컬 스토리지를 자동으로 프로비저닝하여 관리 편의를 제공하지만, 여전히 노드 종속성으로 인한 고가용성 문제는 존재합니다.
두 방식 모두 특정 노드의 로컬 스토리지를 활용한다는 공통점이 있지만, 운영 환경에서 자동화 및 관리의 용이성을 추구한다면 Local Path Provisioner가 더 적합하며, 단순 테스트나 개발 환경에서는 hostPath가 빠른 해결책이 될 수 있습니다.
local-path-provisioner 설치
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.31/deploy/local-path-storage.yaml
배포된 SC 및 설정 확인
kubectl get-all -n local-path-storage
kubectl get pod -n local-path-storage -owide
kubectl describe cm -n local-path-storage local-path-config
kubectl get sc
kubectl describe sc local-path
# 배포
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.31/deploy/local-path-storage.yaml
...
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-path
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
---
apiVersion: v1
kind: ConfigMap
metadata:
name: local-path-config
namespace: local-path-storage
data:
config.json: |-
{
"nodePathMap":[
{
"node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
"paths":["/opt/local-path-provisioner"]
}
]
}
setup: |-
#!/bin/sh
set -eu
mkdir -m 0777 -p "$VOL_DIR"
teardown: |-
#!/bin/sh
set -eu
rm -rf "$VOL_DIR"
...
# 확인
kubectl get-all -n local-path-storage
kubectl get pod -n local-path-storage -owide
kubectl describe cm -n local-path-storage local-path-config
kubectl get sc
kubectl get sc local-path
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path rancher.io/local-path Delete WaitForFirstConsumer false 34s
'AWS' 카테고리의 다른 글
AWS 인스턴스 타입별 가용영역 확인하기 (0) | 2024.06.19 |
---|---|
AWS EBS 성능테스트 (0) | 2023.08.14 |
AWS EBS 재부팅 시 자동 마운트 설정 (0) | 2023.06.04 |
AWS 계정 MFA 분실 및 재활성 가이드 (0) | 2023.05.22 |
AWS EBS 연결 및 마운트 과정 (0) | 2023.05.18 |