카테고리 없음

[Infra] Ingress-nginx 설치 및 ELB 생성(Feat. VPC(public subnet) 생성, 도메인 등록)

처누 2025. 5. 7. 16:04

마스터 노드 구축 및 워커 노드 조인 후 도메인 연결을 위해서 ELB가 필요하여 Ingress-nginx를 설치하고 아래 명령어를 통해 EXTERNAL-IP에 ELB가 생성이 되는지 확인해봤다.

 kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   <IP>             <pending>     80:31608/TCP,443:30432/TCP   39s
ingress-nginx-controller-admission   ClusterIP      <IP>             <none>        443/TCP                      39s

LoadBalancer 타입의 ingress-nginx-controller의 EXTERNAL-IP는 계속 상태였고, 찾아보니 EIP 설정과 퍼블릭 서브넷 설정을 안해줘서 발생하는 문제였다.

kubeadm-config.yaml에 cloud-provider=aws를 추가하여 kubeadm init을 하면 생성이 된다고 해서 시도 했으나 아래와 같은 에러가 발생했다.

May 05 13:36:16 ip-10-0-12-119 kubelet[48097]: Flag --container-runtime-endpoint has been deprecated, This parameter should be set via the config file specified by the Kubelet's --config flag. See https>
May 05 13:36:16 ip-10-0-12-119 kubelet[48097]: Flag --pod-infra-container-image has been deprecated, will be removed in a future release. Image garbage collector will get sandbox image information from >
May 05 13:36:16 ip-10-0-12-119 kubelet[48097]: I0505 13:36:16.203157   48097 server.go:210] "--pod-infra-container-image will not be pruned by the image garbage collector in kubelet and should also be s>
May 05 13:36:16 ip-10-0-12-119 kubelet[48097]: I0505 13:36:16.206170   48097 server.go:489] "Kubelet version" kubeletVersion="v1.30.12"
May 05 13:36:16 ip-10-0-12-119 kubelet[48097]: I0505 13:36:16.206188   48097 server.go:491] "Golang settings" GOGC="" GOMAXPROCS="" GOTRACEBACK=""
May 05 13:36:16 ip-10-0-12-119 kubelet[48097]: E0505 13:36:16.206417   48097 run.go:74] "command failed" err="failed to run Kubelet: unknown cloud provider \"aws\""
May 05 13:36:16 ip-10-0-12-119 systemd[1]: kubelet.service: Main process exited, code=exited, status=1/FAILURE

위 로그에서 발견할 수 있는 점은 아래와 같았다.

  • kubelet이 --cloud-provider=aws를 인식하지 못하는 점
  • cloud-provider: aws 설정은 했지만 Cloud Controller Manager(CCM)을 찾지 못한 점

Kubernetes v1.25부터는 in-tree cloud provider 방식이 deprecated되었고, 현재 나의 버전인 v1.30에서는 완전히 삭제됐다.(아래 Kubernetes 공식 GitHub 참고)
Kubernetes GitHub

따라서 Cloud Controller Manager(CCM)를 배포하여 ELB가 생성되도록 유도해야했다.
Kubernetes AWS Cloud Provider 공식 문서에 설정 방법이 그대로 나와 있으니 따라해보자.
Kubernetes AWS Cloud Provider

1. IAM 정책

CCM이 AWS API와 연동되기 위해서 EC2 인스턴스에 IAM 역할을 부여해줘야 한다. 컨트롤 플레인과 워커 노드에 부여하는 IAM 역할이 다르니 각각 생성하여 부여해주자.

Control Plane(Master Node) Policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "autoscaling:DescribeAutoScalingGroups",
        "autoscaling:DescribeLaunchConfigurations",
        "autoscaling:DescribeTags",
        "ec2:DescribeInstances",
        "ec2:DescribeRegions",
        "ec2:DescribeRouteTables",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSubnets",
        "ec2:DescribeVolumes",
        "ec2:DescribeAvailabilityZones",
        "ec2:CreateSecurityGroup",
        "ec2:CreateTags",
        "ec2:CreateVolume",
        "ec2:ModifyInstanceAttribute",
        "ec2:ModifyVolume",
        "ec2:AttachVolume",
        "ec2:AuthorizeSecurityGroupIngress",
        "ec2:CreateRoute",
        "ec2:DeleteRoute",
        "ec2:DeleteSecurityGroup",
        "ec2:DeleteVolume",
        "ec2:DetachVolume",
        "ec2:RevokeSecurityGroupIngress",
        "ec2:DescribeVpcs",
        "ec2:DescribeInstanceTopology",
        "elasticloadbalancing:AddTags",
        "elasticloadbalancing:AttachLoadBalancerToSubnets",
        "elasticloadbalancing:ApplySecurityGroupsToLoadBalancer",
        "elasticloadbalancing:CreateLoadBalancer",
        "elasticloadbalancing:CreateLoadBalancerPolicy",
        "elasticloadbalancing:CreateLoadBalancerListeners",
        "elasticloadbalancing:ConfigureHealthCheck",
        "elasticloadbalancing:DeleteLoadBalancer",
        "elasticloadbalancing:DeleteLoadBalancerListeners",
        "elasticloadbalancing:DescribeLoadBalancers",
        "elasticloadbalancing:DescribeLoadBalancerAttributes",
        "elasticloadbalancing:DetachLoadBalancerFromSubnets",
        "elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
        "elasticloadbalancing:ModifyLoadBalancerAttributes",
        "elasticloadbalancing:RegisterInstancesWithLoadBalancer",
        "elasticloadbalancing:SetLoadBalancerPoliciesForBackendServer",
        "elasticloadbalancing:AddTags",
        "elasticloadbalancing:CreateListener",
        "elasticloadbalancing:CreateTargetGroup",
        "elasticloadbalancing:DeleteListener",
        "elasticloadbalancing:DeleteTargetGroup",
        "elasticloadbalancing:DescribeListeners",
        "elasticloadbalancing:DescribeLoadBalancerPolicies",
        "elasticloadbalancing:DescribeTargetGroups",
        "elasticloadbalancing:DescribeTargetHealth",
        "elasticloadbalancing:ModifyListener",
        "elasticloadbalancing:ModifyTargetGroup",
        "elasticloadbalancing:RegisterTargets",
        "elasticloadbalancing:DeregisterTargets",
        "elasticloadbalancing:SetLoadBalancerPoliciesOfListener",
        "iam:CreateServiceLinkedRole",
        "kms:DescribeKey"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}

Worker Node Policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeInstances",
        "ec2:DescribeRegions",
        "ecr:GetAuthorizationToken",
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:GetRepositoryPolicy",
        "ecr:DescribeRepositories",
        "ecr:ListImages",
        "ecr:BatchGetImage"
      ],
      "Resource": "*"
    }
  ]
}

2. cloud-provider=external 추가

kubeadm-config.yaml에 선언한 cloud-provider=aws은 모두 삭제하고 마스터 노드에 cloud-provider 플래그와 kube-controller-manger.yaml에 cloud-provider 설정을 해주었다.

sudo sed -i 's/^KUBELET_KUBEADM_ARGS="/KUBELET_KUBEADM_ARGS="--cloud-provider=external /' /var/lib/kubelet/kubeadm-flags.env

#kube-controller-manager.yaml에 cloud-provier=external속성 추가
sudo vi /etc/kubernetes/manifests/kube-controller-manager.yaml

3. tag 추가

AWS Cloud Provider가 인식할 수 있또록 해당 리소스에 다음과 같은 태그를 붙여야 한다.
태그를 붙이지 않으면 리소스를 인식하지 못하여 kubeadm init시에 에러가 발생한다.

tag는 두 가지 속성이 있다.

  • owned : 리소스가 클러스터에 의해 소유되고 관리되는 경우
  • shared : 리소스가 클러스터 간에 공유되고, 클러스터가 삭제되어도 유지되어야 하는 경우

인스턴스, 퍼블릭/프라이빗 서브넷, 라우팅 테이블, 보안 그룹에 tag를 추가해야한다.
ClusterName은 kubeadm-config에서 설정한 값 혹은 아래 명령어로 확인할 수 있다.

kubectl get cm -n kube-system kubeadm-config -o yaml | grep clusterName
  • Instance
    • kubernetes.io/cluster/your_cluster_name : owned/shared
  • Subnet
    • kubernetes.io/cluster/your_cluster_name : owned/shared
    • Public Subnet : kubernetes.io/role/elb: 1
    • Private Subnet : kubernetes.io/role/internal-elb: 1
  • Routing Table
    • Cloud Controller Manager가 --configure-cloud-routes: "false" 옵션으로 시작되지 않는 경우 Subnet처럼 태그를 지정
  • Security Group
    • kubernetes.io/cluster/your_cluster_name : owned/shared

위 설정 완료한 후 Cloud Controller Manager 설치

helm repo add aws-cloud-controller-manager https://kubernetes.github.io/cloud-provider-aws
helm repo update

helm upgrade --install aws-cloud-controller-manager aws-cloud-controller-manager/aws-cloud-controller-manager
  --namespace kube-system
  --set region=ap-northeast-2
  --set serviceAccount.create=true
  --set cloudControllerManager.extraArgs.cluster-name=kubernetes
  --set cloudControllerManager.extraArgs.configure-cloud-routes=false
  --set cloudControllerManager.extraArgs.allocate-node-cidrs=false
  --set cloudControllerManager.extraArgs.aws-region=ap-northeast-2

하지만 ELB는 계속해서 <pending> 상태였고, 인스턴스에 퍼블릭 서브넷 연결이 돼있지 않아 상태가 유지됐다.
VPC는 인스턴스 생성 시에 설정할 수 있기 때문에 현재 인스턴스들을 AMI로 저장해놓고 다시 설치해야하는 번거로움이 있었다.

퍼블릭 서브넷이 필요한 이유

  • ELB는 퍼블릭 서브넷이 있어야 외부에서 접근 가능한 IP를 부여할 수 있다.
  • AWS의 LoadBalancer 타입 서비스는 ELB를 만들때, 퍼블릭 서브넷에 인터페이스를 연결해서 퍼블릭 IP(EXTERNAL-IP)를 발급함.
  • 프라이빗 서브넷만 있으면 ELB는 프라이빗 NLB처럼 만들어지거나 아예 생성이 안되고 <pending> 상태로 남음.

EC2 인스턴스를 생성하면 디폴트로 생성되는 VPC가 있다. 해당 VPC는 private subnet밖에 없기 때문에 VPC를 새로 만들어줘야 한다.

EC2에 접속해 VPC 검색 후 VPC 생성 클릭

IPv4 CIDR은 기본값으로 설정했고(해당 표기법은 각 설정마다 의미가 달라서 나중에 정리할 예정), 가용 영역과 퍼블릭/프라이빗 서브넷 수는 1개씩만 지정해줬다.

VPC 생성 후 조금 기다린 후 서브넷탭에 들어가보면 방금 생성한 VPC에 대한 퍼블릭/프라이빗 서브넷이 생성돼있다.

🌀ELB란???

ELB (Elastic Load Balnacer)는 AWS에서 제공하는 트래픽 분산 시스템.
외부에서 들어오는 요청을 여러 대상(EC2, EKS, K8s Ingress 등)에 자동으로 라우팅하고 분산시켜줌.
종류눈 다음과 같다.

종류 설명
ALB (Application Load Balancer) HTTP/HTTPS 요청 레벨 로드 밸런싱. URL 경로 기반 라우팅 지원.
NLB (Network Load Balancer) 고성능 TCP/UDP 로드 밸런싱. 빠르고 안정적인 네트워크 연결.
CLB (Classic Load Balancer) 예전 방식. 이제는 ALB/NLB 권장.

NLB가 고정 IP, 빠른 성능, L4 레벨 분산 특성 덕분에 Ingress Controller와 함께 자주 사용된다.

인스턴스 생성 시 새로 생성한 VPC 설정 후 마스터 노드 구축, 워커 노드 조인, CNI설치, CCM설치 후 Ingress-nginx 설치!!

Ingress-nginx 설치

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx
  --namespace ingress-nginx
  --set controller.service.type=LoadBalancer # ELB 생성
  --set controller.service.annotations."service\.beta\.kubernetes\.io/aws-load-balancer-type"="nlb" # 어떤 ELB를 만들지 명시
  --set controller.service.annotations."service\.beta\.kubernetes\.io/aws-load-balancer-target-type"="ip" # hostPort 없이 Pod IP에 직접 트래픽 전달
  --set controller.service.externalTrafficPolicy=Cluster
  --set controller.extraArgs.publish-service="ingress-nginx/ingress-nginx-controller" # ELB 주소를 Ingress status에 자동 반영
  --set controller.hostPort.enabled=false

Ingress-nginx를 LoadBalancer타입으로 설치한 이유

Kubernetes 클러스터에 외부 트래픽(인터넷)을 받아들이기 위해서는 클러스터 외부와 통신할 수 있는 진입점이 필요함.
Ingress Controller는 바로 그 역할을 함. 그런데 단독으로는 외부와 연결되지 않기 때문에 Kubernetes Service 리소스를 통해 외부에 노출해야 함.
controller.service.type=LoadBalancer는 다음을 의미한다.

  • Kubernetes가 AWS와 연동해 ELB를 자동 생성하도록 하는 설정임.
  • 생성된 ELB는 Ingress Controller에 연결되어 외부의 요청을 클러스터 내부 서비스로 전달할 수 있게 해줌.

이후 kubectl get svc -n ingress-nginx명령어로 확인해보면 EXTERNAL-IP에 NLB가 생성된 것을 볼 수 있다.

kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP           PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   <IP>             <NLB IP>          80:32695/TCP,443:32558/TCP   23h
ingress-nginx-controller-admission   ClusterIP      <IP>             <none>            443/TCP                      23h

참고
Kubernetes-쿠버네티스-서비스Service-Deep-Dive-4-LoadBalancer
AWS에서 쿠버네티스 클러스터 구축하기
Kubernetes Documents
Kubernetes AWS Cloud Provider