본문 바로가기!

EKS

[EKS] AWS EKS의 Networking (VPC CNI, LB Controller)

728x90
반응형

1.  AWS VPC CNI

1-1)  K8S CNI 란?

  • Container Networking Interface의 약어로, K8S 클러스터 내에서 컨테이너 간 통신을 관리하는 인터페이스입니다.
  • K8S에서는 CNI를 통해 다음과 같은 네트워크 기능을 제공합니다.
    • Pod간 통신, 네트워크 정책 및 보안(pod간 통신 차단 등), 외부 리소스와 통신
  • K8S CNI 플러그인으로는 Calico, Flannel, Weave 등이 있습니다.

 

1-2)  AWS VPC CNI 란?

  • VPC CNI는 AWS에서 제공하는 EKS 클러스터에서 네트워킹을 관리하는 CNI 플러그인입니다.

  • AWS VPC CNI 특징
    • VPC 기반의 네트워킹을 사용하여 pod간 통신을 처리합니다.
    • 보안 그룹 및 ACL을 사용하여 네트워크 보안을 강화할 수 있습니다.
    • 기본적으로 pod가 VPC와 동일한 대역(IP 대역)을 갖습니다.

 

  • AWS에서는 다른 CNI 플러그인과 달리 VPC CNI를 통해 pod에 VPC의 CIDR 범위 내에서 IP 주소를 할당해주기 때문에 pod는 VPC의 서브넷 내의 IP주소를 할당받게 되며, 이러한 구성을 통해 네트워크 설정을 적용하는데 유용하게 활용할 수 있습니다.

  • AWS VPC CNI 주의점
    • 초기 VPC 생성 시 할당한 CIDR은 변경할 수 없습니다.
    • AWS VPC CNI는 기본적으로 pod가 서브넷 내의 IP주소를 할당받게 되는데 기업에서의 운영에서는 많은 트래픽 발생으로 인한 pod 증가, 또는 애플리케이션 증가 등의 이유로 더 많은 pod IP가 필요한 경우가 생기게 됩니다.
    • 즉, 초기 설계와 다르게 더 많은 pod를 생성하여야 하지만 subnet 가용 IP가 부족한 현생을 겪을 수 있습니다.
    • 이럴 경우를 대비하여 VPC에 Secondary CIDR를 추가하고 K8S내 ENI_Config를 수정하여 pod가 사용할 IP대역대를 VPC의 Secondary CIDR 대역대로 설정하여 사용할 수 있습니다.
    • https://repost.aws/knowledge-center/eks-multiple-cidr-ranges

 

1-3)  K8S Calico CNI와 AWS VPC CNI 차이

  • Calico와 같은 기존 CNI들은 pod들간의 통신때 노드간 통신을 위해 패킷 오버레이가 발생해 한번의 오버헤드가 발생하게 됩니다.
  • 하지만 VPC CNI같은 경우 네트워크 통신의 최적화를 위해 노드와 파드의 네트워크 대역을 동일하게 설정함으로 동일 대역으로 직접 통신하기 때문에 pod들간 통신에서 오버헤드가 줄어듭니다.

 

1-4)  Kube-proxy Mode

  • K8S에 kube-proxy Mode는 지속적으로 k8s 업데이트 되면서 변화되었습니다.
  • 개발 초기에는 userspace mode였고 kubernetes 1.19 이상부터는 iptables mode로 default mode로 변경되었습니다.
  • iptables ipvs는 Kubernetes 클러스터 내에서 서비스의 트래픽을 포드로 라우팅하는 방식에 대한 구현 방법입니다.

 

  • iptables
    • iptables는 Linux 시스템에서 네트워크 트래픽을 관리하기 위한 방화벽 및 패킷 필터링 시스템입니다.
    • 기본적으로 Linux 시스템에서 널리 사용되는 방식으로, 패킷을 제어하고 방화벽 규칙을 설정할 수 있습니다.
    • 간단하고 유연한 방식으로 트래픽을 관리할 수 있지만, 대규모 클러스터에서는 성능 문제가 발생할 수 있습니다. 특히 로드 밸런싱과 관련된 작업에서는 성능의 한계를 경험할 수 있습니다.
  • IPVS (IP Virtual Server):
    • IPVS는 Linux 커널에서 제공하는 로드 밸런싱 시스템으로, L4 (Transport Layer) 수준에서의 로드 밸런싱을 위해 설계되었습니다.
    • iptables에 비해 더 향상된 성능과 확장성을 제공합니다. 특히 대규모 네트워크 환경에서 효율적으로 동작하며, 로드 밸런싱을 통해 트래픽을 분산시킬 수 있습니다.
    • IPVS는 커널 내부에서 동작하므로 네트워크 트래픽 처리에 있어서 더 빠르고 효율적입니다.
  • 따라서 iptables는 간단하고 널리 사용되는 방식이지만, 대규모 클러스터나 높은 트래픽을 다루는 경우에는 성능 문제가 발생할 수 있습니다. 이런 경우에는 IPVS와 같은 더 효율적인 로드 밸런싱 시스템을 사용하는 것이 좋습니다. AWS EKS에서는 iptables 모드를 사용하는 것이 기본 설정이며, IPVS 모드를 선택할 수도 있습니다.
  •  
  • AWS EKS에서는 kube-proxy mode는 다음과 같이 확인할 수 있습니다.

 

 

1-5)  노드 간 파드 통신

  • 파드간 통신 흐름 : AWS VPC CNI 경우 별도의 오버레이(Overlay) 통신 기술 없이, VPC Native 하게 파드간 직접 통신이 가능합니다.

  • 노드 간 파드 통신 테스트 
  • 테스트용 파드 생성
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: netshoot-pod
spec:
  replicas: 3
  selector:
    matchLabels:
      app: netshoot-pod
  template:
    metadata:
      labels:
        app: netshoot-pod
    spec:
      containers:
      - name: netshoot-pod
        image: nicolaka/netshoot
        command: ["tail"]
        args: ["-f", "/dev/null"]
      terminationGracePeriodSeconds: 0
EOF

 

  • 파드 이름 변수 지정
PODNAME1=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[0].metadata.name})
PODNAME2=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[1].metadata.name})
PODNAME3=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[2].metadata.name})

 

  • 파드3 Shell 에서 파드1로 ping 테스트
kubectl exec -it $PODNAME3 -- ping -c 2 $PODIP1

 

  • 워커 노드 EC2에서 TCPDUMP 확인
sudo tcpdump -i any -nn icmp

  • 위와 같이 파드간 통신테스트를 통해 별도의 NAT 동작 없이 통신이 가능한것을 확인 가능합니다.

 

1-6)  파드에서 외부 통신

  • iptable 에 SNAT 을 통하여 노드의 eth0 IP로 변경되어서 외부와 통신합니다.
  • VPC CNI 의 External source network address translation (SNAT) 설정에 따라, 외부(인터넷) 통신 시 SNAT 하거나 혹은 SNAT 없이 통신을 할 수 있습니다.

 

  • 파드에서 외부 통신 테스트
  • pod-1 Shell 에서 외부로 ping 테스트
kubectl exec -it $PODNAME1 -- ping -c 1 www.google.com

 

  • 워커 노드 EC2에서 TCPDUMP 확인
sudo tcpdump -i any -nn icmp

 

 

1-7)  노드에 파드 생성 갯수 제한

  • EKS는 워커노드의 인스턴스 타입 별 파드 생성 갯수가 제한됩니다.
  • 인스턴스 타입 별 ENI 최대 갯수와 할당 가능한 최대 IP 갯수에 따라서 파드 배치 갯수가 결정됩니다.
  • 단, aws-node 와 kube-proxy 파드는 호스트의 IP를 사용함으로 최대 갯수에서 제외됩니다.
# t3 타입의 정보(필터) 확인
aws ec2 describe-instance-types --filters Name=instance-type,Values=t3.* \
 --query "InstanceTypes[].{Type: InstanceType, MaxENI: NetworkInfo.MaximumNetworkInterfaces, IPv4addr: NetworkInfo.Ipv4AddressesPerInterface}" \
 --output table

 

 

 

2.  Service & AWS LoadBalancer Controller

2-1)  Service 종류 

  • K8S의 Service 종류는 다음과 같이 3가지가 존재합니다.
  • Cluster IP 타입
    • 클러스터 내부에서만 접근 가능한 Type으로 Service는 클러스터 내부 IP 주소를 할당받아 접근할 수 있습니다.
    • 주로 클러스터 내부에서 통신해야 하는 마이크로서비스들을 연결하는 데 사용됩니다. 외부에서 접근할 필요가 없는 서비스에 이 타입을 사용합니다.

  • NodePort 타입
    •  각 노드의 IP 주소와 지정된 포트를 이용하여 외부에서 접근할 수 있도록 서비스를 노출합니다. 즉, 클러스터 외부에서 Node의 IP 주소와 NodePort를 통해 서비스에 접근할 수 있습니다.
    • 개발 또는 테스트 환경에서 특정 포트를 외부로 노출해야 할 때 사용됩니다. 실 운영에는 보안상 노출 위험이 있기때문에 보통 사용하지 않습니다.

  • LoadBalacer 타입
    • Cloud Provider에서 제공하는 로드 밸런서(Load Balancer)를 사용하여 외부에서 접근할 수 있도록 서비스를 노출합니다.
    • AWS의 Loadbalancer Controller와 같이 Cloud Provider에서 로드 밸런서 리소스를 동적으로 생성하고 서비스에 연결합니다.

 

 

  • Service는 kube-proxy를 통해 클러스터 내부의 트래픽을 라우팅해주며 deployment의 pod가 여러개라도 하나의 Service를 통해 라우팅되며 pod들 중 정상적으로 트래픽 수신이 가능한 pod들을 대상으로 트래픽이 라우팅됩니다.

 

2-2)  AWS Load Balancer mode 

  • AWS Load Balancer에는 Instance modeIP mode를 지원합니다.
  • Instance mode
    • LB로부터 트래픽을 받기 위해서는 서비스가 NodePort 타입으로 노출되어야 합니다.
    • LB를 통해 타겟그룹이 워커노드(Instance)의 Nodeport로 잡혀서 트랙피이 전달되는 방식입니다.
    • externalTrafficPolicy : ClusterIP
      • 2번 분산 및 SNAT으로 Client IP 확인 불가능 ← LoadBalancer 타입 (기본 모드) 동작
    • externalTrafficPolicy : Local
      • 1번 분산 및 ClientIP 유지, 워커 노드의 iptables 사용합니다.
  • IP mode
    • IP mode를 사용하기 위해서는 AWS LoadBalancer Controller 및 정책 설정이 필요합니다. 
    • LB가 Ingress 트래픽을 직접 Kubernetes의 pod로 전달하는 방식입니다.
    • Proxy Protocol v2 비활성화
      • NLB에서 바로 파드로 인입, 단 ClientIP가 NLB로 SNAT 되어 Client IP 확인 불가능합니다.
    • Proxy Protocol v2 활성화
      • NLB에서 바로 파드로 인입 및 ClientIP 확인 가능합니다.
      • (→ 단 PPv2 를 애플리케이션이 인지할 수 있게 설정 필요합니다)

 

 

2-2)  Terraform을 통한 AWS LoadBalancer Controller 배포 with IRSA

  • IRSA(IAM Roles for Service Accounts)는 AWS EKS에서 관리하는 K8S 클러스터에서 실행되는 파드에 Service Account를 사용하여 AWS 서비스에 대한 액세스 권한을 부여하는 방법입니다.
  • LoadBalacer Controller는 위와 같이 IRSA로 Role을 부여받아 AWS의 LoadBalacer를 생성할 수 있습니다.
  • LoadBalacer Controller를 위한 Role을 생성합니다.
data "http" "lb_controller_policy" {
  url = "https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.5.4/docs/install/iam_policy.json"

  request_headers = {
    Accept = "application/json"
  }
}

resource "aws_iam_policy" "lb_controller_policy" {
  name        = "${local.company_name}-lb-controller-policy"
  description = "load balancer controller policy"
  policy      = data.http.lb_controller_policy.response_body
}


data "aws_iam_policy_document" "lb_controller_assume_role_policy" {
  statement {
    sid     = ""
    effect  = "Allow"
    actions = ["sts:AssumeRoleWithWebIdentity"]

    condition {
      test     = "StringEquals"
      variable = "${replace(aws_iam_openid_connect_provider.oidc_cluster.url, "https://", "")}:aud"
      values   = ["sts.amazonaws.com"]
    }

    condition {
      test     = "StringEquals"
      variable = "${replace(aws_iam_openid_connect_provider.oidc_cluster.url, "https://", "")}:sub"
      values   = ["system:serviceaccount:kube-system:aws-load-balancer-controller"]
    }

    principals {
      type        = "Federated"
      identifiers = [aws_iam_openid_connect_provider.oidc_cluster.arn]
    }
  }
}

resource "aws_iam_role" "lb_controller_role" {
  assume_role_policy = data.aws_iam_policy_document.lb_controller_assume_role_policy.json
  name               = "${local.company_name}-lb-controller-role"

  depends_on = [aws_iam_openid_connect_provider.oidc_cluster]

  tags = {
    "ServiceAccount"          = "aws-load-balancer-controller-sa"
    "ServiceAccountNameSpace" = "kube-system"
  }
}

resource "aws_iam_role_policy_attachment" "lb_controller_policy_attach" {
  policy_arn = aws_iam_policy.lb_controller_policy.arn
  role       = aws_iam_role.lb_controller_role.name
}

 

  • Terraform의 Helm Provider를 이용하여 LoadBalacer 컨트롤러를 생성합니다.
  • IRSA를 적용하기 위해 위에서 생성한 Role을 Helm Setting에 추가하여 Helm Install을 진행합니다.
resource "helm_release" "helm_AWSLoadBalancerController" {
  name       = "aws-load-balancer-controller"

  repository = "https://aws.github.io/eks-charts"
  chart      = "aws-load-balancer-controller"
  version    = "1.6.2"

  namespace = "kube-system"

  set {
    name = "image.repository"
    value = "602401143452.dkr.ecr.us-east-1.amazonaws.com/amazon/aws-load-balancer-controller"
  }

  set {
    name  = "serviceAccount.create"
    value = "true"
  }

  set {
    name  = "serviceAccount.name"
    value = "aws-load-balancer-controller"
  }

  set {
    name  = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
    value = module.eks.lb_controller_role_arn
  }

  set {
    name  = "vpcId"
    value = module.vpc.vpc_id
  }

  set {
    name  = "region"
    value = "ap-northeast-2"
  }

  set {
    name  = "clusterName"
    value = module.eks.eks_name
  }

  depends_on = [ module.eks ]
}

 

  • 생성 확인

 

 

2-3) Ingress 란?

  • Ingress는 Web Proxy(HTTP/HTTPS) 역할을 하며 클러스터 내부의 서비스(ClusterIP, NodePort, Loadbalancer)를 외부로 노출할 수 있습니다.

 

2-4) GItLab helm Chart 설치와 Ingress 배포 테스트

  • kube-ops-view 설치
# 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 env.TZ="Asia/Seoul" --namespace kube-system
kubectl patch svc -n kube-system kube-ops-view -p '{"spec":{"type":"LoadBalancer"}}'

# kube-ops-view 접속 URL 확인 (1.5 배율)
kubectl get svc -n kube-system kube-ops-view -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "KUBE-OPS-VIEW URL = http://"$1":8080/#scale=1.5"}'

 

  • LB URL 확인

 

  • GitLab Helm Chart 설치
# repo 등록
helm repo add gitlab https://charts.gitlab.io/
helm repo update

# 압축파일 다운로드
$ helm fetch gitlab/gitlab

# 압축 파일 해제
$ tar xvfz gitlab-7.9.2.tgz

# namespace 생성
$ kubectl create ns gitlab

# /gitlab/templates/NOTES.txt의 아래부분 삭제
# https://docs.gitlab.com/charts/development/deprecations.html
 {{ include "gitlab.deprecations" . }}
 
# gitlab 설치
helm install gitlab -f values-dev.yaml ./ -n gitlab

 

  • Ingress 생성
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: gitlab
  name: gitlab
  annotations:
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443}]'
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/subnets: "{Subnet ID입력}"
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/backend-protocol: 'HTTP'
    alb.ingress.kubernetes.io/group.name: gitlab 
    alb.ingress.kubernetes.io/group.order: '2'
    alb.ingress.kubernetes.io/certificate-arn: "{ACM 인증서 ARN입력}"
spec:
  ingressClassName: alb
  rules:
    - host: gitlab.junholee.site
      http:
        paths:
          - path: "/"
            pathType: Prefix
            backend:
              service:
                name: gitlab-webservice-default
                port:
                  number: 8181

 

  • kube-ops-view 에서 생성되는 pod 확인

 

  • Ingress 확인 및 ALB URL 접속 확인

 

728x90
반응형

'EKS' 카테고리의 다른 글

[EKS] Security  (0) 2024.04.14
[EKS] Autoscaling  (0) 2024.04.06
[EKS] Observability  (0) 2024.03.28
[EKS] AWS EKS의 Storage & Nodegroup  (1) 2024.03.23
[EKS] AWS EKS의 특징과 Cluster Endpoint 통신 방식  (0) 2024.03.09