Quantcast
Channel: 개발 노트
Viewing all articles
Browse latest Browse all 299

[Kubernetes] Amazon EKS 운영 시 IP가 부족하다면?

$
0
0
<h4 data-ke-size="size20">Challenge</h4> <p data-ke-size="size16">Amazon EKS 기반으로 서비스를 운영하다보면 Pod 수가 점점 늘어나면서 최초에 설계한 IP Range를 초과하는 경우가 있고, 마이크로서비스 기반 아키텍처가 인기를 얻으면서 더욱 빈번하게 발생합니다. 이 경우 서브넷의 IP 크기는 조정할 수 없기 때문에 새로운 서브넷을 생성하여 노드를 이전해야하고, VPC의 IP가 부족한 경우에는 서브넷을 확장할 수도 없게 됩니다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1049" data-origin-height="542"><span data-url="https://blog.kakaocdn.net/dn/b6JUQ7/btszKxpExOX/YvVy4nKnuXLR3jRllsVlW1/img.png" data-lightbox="lightbox"><img src="https://blog.kakaocdn.net/dn/b6JUQ7/btszKxpExOX/YvVy4nKnuXLR3jRllsVlW1/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6JUQ7%2FbtszKxpExOX%2FYvVy4nKnuXLR3jRllsVlW1%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" data-origin-width="1049" data-origin-height="542"/></span></figure> </p> <p data-ke-size="size16"> </p> <h4 data-ke-size="size20">Amazon EKS를 사용할 때 IP가 얼마나 필요할까?</h4> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1049" data-origin-height="731"><span data-url="https://blog.kakaocdn.net/dn/bMIlPc/btszI1Y7gwC/Ka9KfOBNgE2aKFWBCDJSE0/img.png" data-lightbox="lightbox"><img src="https://blog.kakaocdn.net/dn/bMIlPc/btszI1Y7gwC/Ka9KfOBNgE2aKFWBCDJSE0/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMIlPc%2FbtszI1Y7gwC%2FKa9KfOBNgE2aKFWBCDJSE0%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" data-origin-width="1049" data-origin-height="731"/></span></figure> </p> <ul style="list-style-type: disc;" data-ke-list-type="disc"> <li>클러스터 생성 시 각 서브넷에는 Amazon EKS에서 사용할 IP 주소가 6개 이상 필요 (16개 이상 권장)</li> <li>EKS는 클러스터 생성 중에 지정된 각 서브넷(클러스터 서브넷이라고도 함)에 X-ENI 생성</li> <li>Kubernetes 버전의 클러스터를 업데이트하면 Amazon EKS에서는 클러스터가 생성한 원래 네트워크 인터페이스를 삭제하고 새로운 네트워크 인터페이스를 생성 <ul style="list-style-type: disc;" data-ke-list-type="disc"> <li>최대 5개의 IP가 추가로 필요하고, 업데이트 완료 시 기존 네트워크 인터페이스가 제거되므로 회수됨 (<a href="https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/update-cluster.html">참고</a>)</li> </ul> </li> <li>만약 서브넷 설정을 변경하는 것으로 해소가 된다면 2023년 10월 24일 부터 클러스터의 서브넷과 보안그룹 수정 가능 (<a href="https://aws.amazon.com/ko/about-aws/whats-new/2023/10/amazon-eks-modification-cluster-subnets-security/">참고</a>)</li> </ul> <p data-ke-size="size16">EC2 인스턴스 유형 별로 실행 가능한 Pod 수 확인</p> <pre id="code_1699097920582" class="bash" data-ke-language="bash" data-ke-type="codeblock"><code>curl -o max-pods-calculator.sh https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/max-pods-calculator.sh chmod +x max-pods-calculator.sh ./max-pods-calculator.sh --instance-type m5.large --cni-version 1.9.0 --cni-prefix-delegation-enabled</code></pre> <p data-ke-size="size16"> </p> <h4 data-ke-size="size20">어떻게 IP 고갈 문제를 해결할 수 있을까?</h4> <p data-ke-size="size16"><b> 1. 더 큰 IP Range를 가진 VPC에 신규 EKS 클러스터 생성</b></p> <ul style="list-style-type: disc;" data-ke-list-type="disc"> <li>VPC CIDR의 최대 크기는 /16 (65k) → eksctl로 클러스터 생성 시 기본값</li> <li>IPv6 기반으로 VPC 생성 시 /56(2^72)</li> </ul> <p data-ke-size="size16"> </p> <p data-ke-size="size16"><b>2. VPC Secondary CIDR 추가 (Custom Networking)</b></p> <p data-ke-size="size16">기본적으로 Amazon VPC CNI는 지정한 서브넷의 IP Range 범위의 IP를 Pod에 할당합니다. 만약 CIDR 범위가 너무 작은 경우에 IP 고갈 문제로 인해 Pod에 할당할 충분한 Secondary IP를 확보하지 못할 수 있습니다.</p> <p data-ke-size="size16">Custom Networking은 IP 고갈 문제를 해결할 수 있는 방법 중에 하나이고, VPC의 Secondary CIDR 기능을 사용합니다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1001" data-origin-height="446"><span data-url="https://blog.kakaocdn.net/dn/zbF4Z/btszMcEFMAm/PS4PplCFroJEkF3KyguhUK/img.png" data-lightbox="lightbox"><img src="https://blog.kakaocdn.net/dn/zbF4Z/btszMcEFMAm/PS4PplCFroJEkF3KyguhUK/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzbF4Z%2FbtszMcEFMAm%2FPS4PplCFroJEkF3KyguhUK%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" data-origin-width="1001" data-origin-height="446"/></span></figure> </p> <p data-ke-size="size16"> </p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="2404" data-origin-height="1337"><span data-url="https://blog.kakaocdn.net/dn/o6Ttp/btszKb7QoGr/dwoTUOIUf9QhxT9fqPcmWk/img.png" data-lightbox="lightbox"><img src="https://blog.kakaocdn.net/dn/o6Ttp/btszKb7QoGr/dwoTUOIUf9QhxT9fqPcmWk/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo6Ttp%2FbtszKb7QoGr%2FdwoTUOIUf9QhxT9fqPcmWk%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" data-origin-width="2404" data-origin-height="1337"/></span></figure> </p> <p data-ke-size="size16"> </p> <ul style="list-style-type: disc;" data-ke-list-type="disc"> <li>참고 : <a href="https://d1.awsstatic.com/architecture-diagrams/ArchitectureDiagrams/expose-microservices-using-eks-ra.pdf?did=wp_card&trk=wp_card">https://d1.awsstatic.com/architecture-diagrams/ArchitectureDiagrams/expose-microservices-using-eks-ra.pdf?did=wp_card&trk=wp_card</a></li> </ul> <p data-ke-size="size16">Custom Networking으로 Secondary CIDR를 사용하게 되면 해당 서브넷에서 생성되는 Pod들은 기본 네트워크 인터페이스에 할당된 IP 주소가 Pod에 할당되는 것이 아니라 Secondary 네트워크 인터페이스의 IP 주소가 할당됩니다.</p> <p data-ke-size="size16"><b>중요:</b> 특정 상황에서 Amazon EKS는 클러스터가 생성된 후 VPC에 추가된 추가 CIDR 블록에서 서브넷에서 시작된 노드와 통신할 수 없습니다. 기존 클러스터에 CIDR 블록을 추가하여 발생하는 업데이트된 범위는 표시되는 데 5시간까지 걸릴 수 있습니다.</p> <ul style="list-style-type: disc;" data-ke-list-type="disc"> <li>VPC에 Secondary CIDR 추가 시 적용되는 규칙 <ul style="list-style-type: disc;" data-ke-list-type="disc"> <li>허용되는 블록 크기는 /16 ~ /28</li> <li>기존 CIDR과 겹치지 않아야함</li> <li>최대 5개의 CIDR를 추가 할 수 있음</li> <li>VPC Peering/DX 사용 시 CIDR 블록이 겹치지 않아야함</li> </ul> </li> </ul> <p data-ke-size="size16"> </p> <p data-ke-size="size16"><b>3. AWS VPC CNI의 Warm Pool 파라미터 최적화</b></p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1790" data-origin-height="677"><span data-url="https://blog.kakaocdn.net/dn/bxVxZB/btszKEIH0pM/xXZGmw3GFl5ue39d0KKeu1/img.png" data-lightbox="lightbox"><img src="https://blog.kakaocdn.net/dn/bxVxZB/btszKEIH0pM/xXZGmw3GFl5ue39d0KKeu1/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxVxZB%2FbtszKEIH0pM%2FxXZGmw3GFl5ue39d0KKeu1%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" data-origin-width="1790" data-origin-height="677"/></span></figure> </p> <p data-ke-size="size16">AWS VPC CNI는 노드(EC2 인스턴스)에 Warm Pool 크기만큼 IP를 사전에 확보해 놓습니다. 이런 이유로 Pod가 실제 사용하지 않더라도 VPC IP를 점유하기 때문에 Subnet에 가용한 IP 수가 부족해질 수 있습니다.</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1796" data-origin-height="907"><span data-url="https://blog.kakaocdn.net/dn/D4xUR/btszNS61Awk/Bw0SnJ5rU6hkGnZkGCazs0/img.png" data-lightbox="lightbox"><img src="https://blog.kakaocdn.net/dn/D4xUR/btszNS61Awk/Bw0SnJ5rU6hkGnZkGCazs0/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD4xUR%2FbtszNS61Awk%2FBw0SnJ5rU6hkGnZkGCazs0%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" data-origin-width="1796" data-origin-height="907"/></span></figure> </p> <p data-ke-size="size16"> </p> <p data-ke-size="size16">풀의 ENI 수와 IP 주소는 <a href="https://github.com/aws/amazon-vpc-cni-k8s/blob/master/docs/eni-and-ip-target.md">WARM_ENI_TARGET, WARM_IP_TARGET, MINIMUM_IP_TARGET</a> 환경 변수로 설정합니다. VPC CNI는 ENI가 부족하면 EC2에 API를 호출하여 ENI를 추가합니다.</p> <ul style="list-style-type: disc;" data-ke-list-type="disc"> <li>WARM_ENI_TARGET : 기본 ENI 외 웜 대기 상태의 ENI 수. 웜 상태의 ENI의 IP가 Pod에 할당되면 더이상 웜상태가 아니기 때문에 새로운 ENI 생성 <ul style="list-style-type: disc;" data-ke-list-type="disc"> <li>만약 1로 설정할 경우 최초 노드에는 두개의 ENI가 attach됨 (기본값)</li> </ul> </li> <li>WARM_IP_TARGET : Pod에 할당되지 않고, 웜 대기 상태로 유지해야할 IP 개수 <ul style="list-style-type: disc;" data-ke-list-type="disc"> <li>실제 할당된 IP와 웜 상태의 IP수의 합이 ENI가 제공하는 IP 수를 초과할 때만 ENI를 추가</li> </ul> </li> <li>MINIMUM_IP_TARGET : 언제든 할당 할 수 있는 최소 IP 수 <ul style="list-style-type: disc;" data-ke-list-type="disc"> <li>WARM_ENI나 WARM_IP 설정과 상관없이 최소로 지정된 IP 수를 맞추기 위해 ENI를 즉시 추가함</li> </ul> </li> </ul> <p data-ke-size="size16">위 설정을 변경하려면 아래와 같이 AWS VPC CNI의 환경 변수 값을 수정해야합니다.</p> <pre id="code_1699098251822" class="bash" data-ke-language="bash" data-ke-type="codeblock"><code>kubectl set env daemonset aws-node -n kube-system MINIMUM_IP_TARGET=5</code></pre> <p data-ke-size="size16"> </p> <p data-ke-size="size16"> </p> <h4 data-ke-size="size20">실습 - EKS Custom Networking으로 추가 IP 확보하기</h4> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1797" data-origin-height="803"><span data-url="https://blog.kakaocdn.net/dn/bidSzm/btszMcEFPZ5/5pNIfRQwXPfaMVcPdBfms0/img.png" data-lightbox="lightbox"><img src="https://blog.kakaocdn.net/dn/bidSzm/btszMcEFPZ5/5pNIfRQwXPfaMVcPdBfms0/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbidSzm%2FbtszMcEFPZ5%2F5pNIfRQwXPfaMVcPdBfms0%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" data-origin-width="1797" data-origin-height="803"/></span></figure> </p> <p data-ke-size="size16"><b>Secondary CIDR 추가하기</b></p> <pre id="code_1699098323927" class="bash" data-ke-language="bash" data-ke-type="codeblock"><code>vpc_id=$(aws eks describe-cluster --name <클러스터명> --query "cluster.resourcesVpcConfig.vpcId" --output text) aws ec2 describe-vpcs --vpc-ids $vpc_id \ --query 'Vpcs[*].CidrBlockAssociationSet[*].{CIDRBlock: CidrBlock, State: CidrBlockState.State}' --out table # Secondary CIDR 블록으로 100.64.0.0/16 추가 aws ec2 associate-vpc-cidr-block --vpc-id $vpc_id --cidr-block 100.64.0.0/16 # 아래 명령 실행 결과 새 CIDR 블록이 associated 상태가 될 때까지 대기 aws ec2 describe-vpcs --vpc-ids $vpc_id --query 'Vpcs[*].CidrBlockAssociationSet[*].{CIDRBlock: CidrBlock, State: CidrBlockState.State}' --out table # Secondary CIDR의 서브넷 생성 export az_1=ap-northeast-2a export az_2=ap-northeast-2b export az_3=ap-northeast-2c new_subnet_id_1=$(aws ec2 create-subnet --cidr-block 100.64.0.0/19 --vpc-id $vpc_id --availability-zone $az_1 | jq -r .Subnet.SubnetId) new_subnet_id_2=$(aws ec2 create-subnet --cidr-block 100.64.32.0/19 --vpc-id $vpc_id --availability-zone $az_2 | jq -r .Subnet.SubnetId) new_subnet_id_3=$(aws ec2 create-subnet --cidr-block 100.64.64.0/19 --vpc-id $vpc_id --availability-zone $az_3 | jq -r .Subnet.SubnetId) # 생성된 서브넷 확인 aws ec2 describe-subnets --filters "Name=vpc-id,Values=$vpc_id" \ --query 'Subnets[*].{SubnetId: SubnetId,AvailabilityZone: AvailabilityZone,CidrBlock: CidrBlock}' \ --output table</code></pre> <p data-ke-size="size16"> </p> <p data-ke-size="size16"><b>Kubernetes 리소스 구성</b></p> <pre id="code_1699098344725" class="bash" data-ke-language="bash" data-ke-type="codeblock"><code>cluster_name=eks-demo # Custom Network를 사용하도록 AWS VPC CNI 설정 변경 kubectl set env daemonset aws-node -n kube-system AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG=true # EKS 클러스터에 할당된 Security Group 확인 cluster_security_group_id=$(aws eks describe-cluster --name $cluster_name --query cluster.resourcesVpcConfig.clusterSecurityGroupId --output text) # Pod를 배포하려는 Subnet에 기존 노드들과 통신을 위한 ENI를 제어하도록 ENIConfig 생성 cat >$az_1.yaml <<EOF apiVersion: crd.k8s.amazonaws.com/v1alpha1 kind: ENIConfig metadata: name: $az_1 spec: securityGroups: - $cluster_security_group_id subnet: $new_subnet_id_1 EOF cat >$az_2.yaml <<EOF apiVersion: crd.k8s.amazonaws.com/v1alpha1 kind: ENIConfig metadata: name: $az_2 spec: securityGroups: - $cluster_security_group_id subnet: $new_subnet_id_2 EOF cat >$az_3.yaml <<EOF apiVersion: crd.k8s.amazonaws.com/v1alpha1 kind: ENIConfig metadata: name: $az_3 spec: securityGroups: - $cluster_security_group_id subnet: $new_subnet_id_3 EOF kubectl apply -f $az_1.yaml kubectl apply -f $az_2.yaml kubectl apply -f $az_3.yaml # ENIConfig 생성 확인 kubectl get ENIConfigs # 신규 노드에 ENIConfig 적용 kubectl set env daemonset aws-node -n kube-system ENI_CONFIG_LABEL_DEF=topology.kubernetes.io/zone</code></pre> <p data-ke-size="size16"> </p> <p data-ke-size="size16">Secondary CIDR로 서브넷을 생성하면 아래와 같이 기존 VPC로 접근할 수있는 라우팅 테이블 규칙이 기본으로 추가됨</p> <p><figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1820" data-origin-height="817"><span data-url="https://blog.kakaocdn.net/dn/cf5QPF/btszLsVh8kX/SEShyzcwqzkhKHfLNKPl7K/img.png" data-lightbox="lightbox"><img src="https://blog.kakaocdn.net/dn/cf5QPF/btszLsVh8kX/SEShyzcwqzkhKHfLNKPl7K/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcf5QPF%2FbtszLsVh8kX%2FSEShyzcwqzkhKHfLNKPl7K%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" data-origin-width="1820" data-origin-height="817"/></span></figure> </p> <p data-ke-size="size16"> </p> <p data-ke-size="size16"><b>신규 노드 그룹에 할당할 IAM Role 생성</b></p> <p data-ke-size="size16">기존 노드 그룹이 사용하는 IAM Role을 그대로 사용하는 경우</p> <pre id="code_1699098422719" class="bash" data-ke-language="bash" data-ke-type="codeblock"><code>node_role_arn=$(aws eks describe-nodegroup --cluster-name $cluster_name --nodegroup-name <기존 노드 그룹 이름> --query "nodegroup.nodeRole" --output text)</code></pre> <p data-ke-size="size16"> </p> <p data-ke-size="size16">IAM Role을 신규 생성하는 경우</p> <pre id="code_1699098436678" class="bash" data-ke-language="bash" data-ke-type="codeblock"><code>cat >node-role-trust-relationship.json <<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF export node_role_name=CustomNetworkingAmazonEKSNodeRole node_role_arn=$(aws iam create-role --role-name $node_role_name --assume-role-policy-document file://"node-role-trust-relationship.json" \ --query Role.Arn --output text) aws iam attach-role-policy \ --policy-arn arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy \ --role-name $node_role_name aws iam attach-role-policy \ --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly \ --role-name $node_role_name aws iam attach-role-policy \ --policy-arn arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy \ --role-name $node_role_name</code></pre> <p data-ke-size="size16"> </p> <p data-ke-size="size16"><b>신규 노드 그룹 생성</b></p> <pre id="code_1699098457911" class="bash" data-ke-language="bash" data-ke-type="codeblock"><code>cat << EOF > create_nodegroup.yaml --- apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: # EKS 클러스터명 name: $cluster_name region: ap-northeast-1 managedNodeGroups: # 노드그룹명 - name: $nodegroup_name labels: { type: customnetworking } privateNetworking: true desiredCapacity: 2 instanceType: $instance_type subnets: - $new_subnet_id_1 - $new_subnet_id_2 - $new_subnet_id_3 ssh: enableSsm: true EOF eksctl create nodegroup -f create_nodegroup.yaml # 노드 생성될 때까지 대기 aws eks wait nodegroup-active --cluster-name $cluster_name --nodegroup-name $nodegroup_name # 노드 생성 확인 kubectl get nodes -L eks.amazonaws.com/nodegroup # Pod 재생성 kubectl rollout deployment <Deployment명> # Pod 확인 kubectl get pods -o wide</code></pre> <p data-ke-size="size16"> </p> <p data-ke-size="size16">이 후 신규 노드에 Pod가 배치되면서 추가된 IP를 사용하게 됩니다.</p>

Viewing all articles
Browse latest Browse all 299

Trending Articles