데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      Kubernetes Cluster 에서 Cloud Provider 로 AWS LoadBalancer 연결하기

      seungkyua 23.02.01
      2,361 15 0

      Kubernetes 에 서비스를 올릴 때 Service 의 Type 으로 LoadBalancer 를 선택하면 cloud 에서 자동으로 LB (external-ip ) 가 생성되어 서비스 pod 에 연결된다.

      어떻게 Kubernetes 에서 설정한 값이 cloud 에 연결될까? 이는 Cloud Provider 가 있어 가능하다.

      Cloud Provider 는 초기에 Kubernetes Controller 에 포함되어 있다.

      하지만 지금은 External Kubernetes Cloud Provider 로 Kubernetes 에서 제외되었으며, 이전 Kubernetes Controller 에 포함된 Cloud Provider 는 Legacy Cloud Provider 로 불리고 있다.

      AWS Cloud Provider 의 경우에는 아직 1.23 (Kubernetes 와 같이 버전을 맞춰가고 있음) alpha 버전이라 아직은 Legacy Cloud Provider 를 사용하는 것이 안정적이다.


      AWS에서는 아래의 순서대로 적용한다.

      1. IAM Policy, Role 생성

      2. VPC, Subnet, Routing Table, Internet Gateway, Nat Gateway 생성

      3. VM 생성

      4. AWS resource 에 Tag 적용

      5. Kubernetes Cluster 생성

      6. Nginx 로 테스트


      1. IAM Policy, Role 생성

      Control plane 과 Node 2개의 Policy 를 생성한다.

      Control Plane Policy

      control node 에서 사용할 policy 이다.


      정책명: control-plane.cluster-api-provider-aws.sigs.k8s.io

      {
        "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",
              "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": [
              "*"
            ]
          }
        ]
      }

      Node Policy

      일반 Node 에 대한 policy 이다.


      정책명: nodes.cluster-api-provider-aws.sigs.k8s.io

      {
        "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": "*"
          }
        ]
      }

      policy 를 생성했으면 이제 Role 을 생성한다.

      Control plane Role

      Policy 를 활용할 수 있는 Role 을 만들어서 Policy 와 연결한다.


      Role 명: control-plane.cluster-api-provider-aws.sigs.k8s.io

      연결할 Policy 리스트
      - control-plane.cluster-api-provider-aws.sigs.k8s.io
      - nodes.cluster-api-provider-aws.sigs.k8s.io

      Node Role

      Role 명: nodes.cluster-api-provider-aws.sigs.k8s.io

      연결할 Policy 리스트
      - nodes.cluster-api-provider-aws.sigs.k8s.io


      2. VPC, Subnet, Routing Table, Internet Gateway, Nat Gateway 생성

      서울 리전의 경우 VPC 를 1개 만들고, public 용도의 subnet 4개, private 용도의 subnet 4개를 만든다.

      Internet Gateway 1개를 만들어서 public subnet 에 연결하고, Nat Gateway 4개를 만들어서 각각 private subnet 에 연결한다.

      Routing Table 은 subnet 갯수에 맞는 8개를 만들어서 각각 연결한다.

      private 용 4개의 Routing table 은 0.0.0.0/0 → nat gateway 를 대상으로 설정하고, public 용 4개의 Routing table 은 0.0.0.0/0 → internet gateway 대상으로 설정한다.

      1. vpc : 1개
         - vpc
      
      2. subent : 8개
         - public-subnet-a
         - public-subnet-b
         - public-subnet-c
         - public-subnet-d
         - private-subnet-a
         - private-subnet-b
         - private-subnet-c
         - private-subnet-d
      
      3. internat gateway : 1개
         - igw
      
      4. Nat Gateway : 4개
         - nat-private-a
         - nat-private-b
         - nat-private-c
         - nat-private-d
      
      5. Routing Table : 4개
         - rt-public-a (0.0.0.0/0 -> igw)
         - rt-public-b (0.0.0.0/0 -> igw)
         - rt-public-c (0.0.0.0/0 -> igw)
         - rt-public-d (0.0.0.0/0 -> igw)
         - rt-priabe-a (0.0.0.0/0 -> nat-private-a)
         - rt-priabe-b (0.0.0.0/0 -> nat-private-b)
         - rt-priabe-c (0.0.0.0/0 -> nat-private-c)
         - rt-priabe-d (0.0.0.0/0 -> nat-private-d)


      3. VM 생성

      VM 은 Controler Plane 3대는 각 private subnet 에 1대씩 생성하고(subnet 1개는 남는다), Node 용 4대는 각 private subnet 1대씩 생성하다.


      bastion 노드로 public subnet 에 1대 생성한다.

      1. bastion VM 1대
         - public-subnet-a
      
      2. Control Plane VM 3대
         - private-subnet-a
         - private-subnet-b
         - private-subnet-c
      
      3. Node VM 4대
         - private-subnet-a
         - private-subnet-b
         - private-subnet-c
         - private-subnet-d


      4. AWS Resource 에 Tag 설정

      aws cloud provider 가 리소스를 파악하기 위해서는 aws 에 적절한 값을 설정해야 한다.

      4-1. VM 에 IAM Role 을 할당

      VM 에서 권한을 얻기 위해서는 반드시 IAM Role 을 할당해야 한다.


      Control node 에는 control-plane.cluster-api-provider-aws.sigs.k8s.io role 을 할당한다.

      aws-1.png


      일반 Node 에는 nodes.cluster-api-provider-aws.sigs.k8s.io role 을 할당한다.


      aws-2.png

      4-2 VM 에 Tag 설정

      VM 에는 Kubernetes Cluster Name 을 Tag 로 지정한다. kubernetes.io/cluster/<cluster name> 과 같이 지정하는데 Cluster Name 은 Kubernetes 를 설치할 때 지정할 수 있다. 기본 값은 cluster.local 인데 여기서는 ahnsk 로 이름을 지정하였다.


      그리고 vm 의 역할을 지정해야 하는데 Controler Node 는 control-plane 으로, Node 는 node 로 지정한다.

      aws-3.png

      aws-4.png

      4-3. Subnet 에 Tag 설정

      Load Balancer 를 핸들링 하기 위해서 Kubernetes Cluster 가 어느 Subnet 과 Routing Table 을 사용해야 하는지 알아야 한다.


      주의해야 할 점은 public subnet 의 경우 [kubernetes.io/role/elb](http://kubernetes.io/role/elb) 이지만, private subnet 의 경우에는 [kubernetes.io/role/internal-elb](http://kubernetes.io/role/internal-elb) 로 해야 한다.

      aws-5.png

      4-4. Routing Table 에 Tag 설정

      aws-6.png


      5. Kubernetes Cluster 생성

      Kubernetes cluster 이름은 앞에서 설명했듯이 ahnsk 로 설정한다.

      kubeadm 혹은 kubespray 를 사용할 수 있으며 여기서 생성 방법을 생략한다.

      aws cloud provider 를 활성화 하기 위해서는 API Server, Controller, Kubelet 에 --cloud-provider=aws 옵션을 추가해야 한다.

      kube-apiserver.yaml

      apiVersion: v1
      kind: Pod
      metadata:
        annotations:
          kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 172.31.22.52:6443
        creationTimestamp: null
        labels:
          component: kube-apiserver
          tier: control-plane
        name: kube-apiserver
        namespace: kube-system
      spec:
        containers:
        - command:
          - kube-apiserver
          - --advertise-address=172.31.22.52
      ...
          **- --cloud-provider=aws**
      ...
          image: registry.k8s.io/kube-apiserver:v1.24.6
      ...

      kube-controller-manager.yaml

      apiVersion: v1
      kind: Pod
      metadata:
        creationTimestamp: null
        labels:
          component: kube-controller-manager
          tier: control-plane
        name: kube-controller-manager
        namespace: kube-system
      spec:
        containers:
        - command:
          - kube-controller-manager
          - --allocate-node-cidrs=true
      ...
          **- --cloud-provider=aws
      ...**
          image: registry.k8s.io/kube-controller-manager:v1.24.6
      ...

      kubelet.env

      ...
      KUBELET_CLOUDPROVIDER="**--cloud-provider=aws --cloud-config=/etc/kubernetes/cloud_config**"
      ...

      kubelet 은 aws 의 리소스를 위해 추가해야 할 값들이 있는데 cloud_config 을 만들어서 옵션을 전달하였다.

      [Global]
      zone=
      vpc=vpc-b342d5d8
      subnetId=
      routeTableId=
      roleArn=
      kubernetesClusterTag=ahnsk
      kubernetesClusterId=ahnsk
      disableSecurityGroupIngress=false
      disableStrictZoneCheck=false
      elbSecurityGroup=

      값을 다 채워 넣으면 apiserver 가 kube-apiserver-master-dummy 라는 이름으로 잘못 실행되므로 조심해야 한다. master-dummy 로 띄우는 방법은 aws account 가 다를 경우에만 사용하는 방법이다. 이는 아래 소스를 보면 알 수 있다.

      https://github.com/kubernetes/legacy-cloud-providers/blob/707ecda639b086132369678680a1b34d4d2b5c7c/aws/aws.go#L1251
      
      ...
        tagged := cfg.Global.KubernetesClusterTag != "" || cfg.Global.KubernetesClusterID != ""
      	if cfg.Global.VPC != "" && (cfg.Global.SubnetID != "" || cfg.Global.RoleARN != "") && tagged {
      		// When the master is running on a different AWS account, cloud provider or on-premise
      		// build up a dummy instance and use the VPC from the nodes account
      		klog.Info("Master is configured to run on a different AWS account, different cloud provider or on-premises")
      		awsCloud.selfAWSInstance = &awsInstance{
      			nodeName: "master-dummy",
      			vpcID:    cfg.Global.VPC,
      			subnetID: cfg.Global.SubnetID,
      		}
      		awsCloud.vpcID = cfg.Global.VPC
      	} else {
      		selfAWSInstance, err := awsCloud.buildSelfAWSInstance()
      		if err != nil {
      			return nil, err
      		}
      		awsCloud.selfAWSInstance = selfAWSInstance
      		awsCloud.vpcID = selfAWSInstance.vpcID
      	}
      ...

      RoleARN 의 값을 넣으면 안되는데 값이 없으면서도 어떻게 kubelet 이 해당 Role 로 인증을 받을 수 있을까? 이는 앞에서 설명한 VM 에 IAM Role 인 control-plane.cluster-api-provider-aws.sigs.k8s.io 이나 nodes.cluster-api-provider-aws.sigs.k8s.io 이 설정되어 있기 때문에 가능하다.

      kubelet-config.yaml

      kubernetes node 정보에 providerID 값이 들어가 있어야 한다. 만약 이 정보가 없다면 LoadBalancer 가 생성된다고 하더라도 인스턴스가 LoadBalancer 에 할당되지 않아 제대로 사용할 수 가 없다.


      providerID 는 kubelet-config.yaml 에 추가한다. aws:///<zone-id>/<instance-id> 값으로 추가한다.

      $ sudo vi /etc/kubernetes/kubelet-config.yaml
      ...
      providerID: "aws:///ap-northeast-2d/i-0f59f059e2d64213f"
      ...

      이미 노드가 생성된 경우에는 해당 값을 변경하고 kubelet 서비스를 다시 restart 한다고 값이 추가되지는 않는다. 그래서 patch 명령으로 동적으로 추가하는 것이 좋다.

      $ kubectl patch node ip-172-31-46-179.ap-northeast-2.compute.internal -p '{"spec": {"providerID": "aws:///ap-northeast-2d/i-08cb6f884239f894c"}}'


      6. Nginx 로 테스트

      nginx 를 생성하고 service 를 LoadBalancer type 으로 생성하여 잘 접속이 되는지 확인해 보자.

      $ kubectl create deploy nginx --image=nginx
      deployment.apps/nginx created
      $ kubectl get pods
      NAME                    READY   STATUS    RESTARTS   AGE
      nginx-8f458dc5b-nhkfd   1/1     Running   0          55s
      $ kubectl expose deployment nginx --name nginx-svc --target-port=80 --port=80 --type=LoadBalancer
      $ kubectl get svc
      NAME         TYPE           CLUSTER-IP     EXTERNAL-IP                                                                   PORT(S)        AGE
      kubernetes   ClusterIP      10.233.0.1     <none>                                                                        443/TCP        5h28m
      nginx-svc    LoadBalancer   10.233.7.131   a76a315f5d7c14652a4db83cc3b25125-113127685.ap-northeast-2.elb.amazonaws.com   80:30796/TCP   27s

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      seungkyua 님의 최신 블로그

      더보기
      동영상 기고하기