[AEWS 2기] EKS Networking

EKS 스터디 CloudNet@팀의 AEWS 2기에 작성된 자료를 베이스로 작성된 블로깅입니다.

저는 쿠버네티스를 접하고 사용안하고, 공부안한지 너무 오래되어서 개념을 익힐겸 핸즈온처럼 하나하나 해보았습니다.

 

포스팅 인덱스는 다음과 같습니다.

craft에서 정리한 자료는 다음과 같습니다. 

https://www.craft.me/s/TYsr7NuyxI3pes


 

노드 IP 확인 및 PrivateIP 변수 지정

원스택 배포 후 이렇게 1개의 bastion host 인스턴스와, 3개의 노드그룹이 생성되었습니다. 

그리고 VS Code를 사용해서 bastion host 인스턴스(작업용 ec2)에 ssh 접속을 해줍니다. 

 

# 노드 IP 확인 및 PrivateIP 변수 지정
N1=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2a -o jsonpath={.items[0].status.addresses[0].address})
N2=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2b -o jsonpath={.items[0].status.addresses[0].address})
N3=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2c -o jsonpath={.items[0].status.addresses[0].address})
echo "export N1=$N1" >> /etc/profile
echo "export N2=$N2" >> /etc/profile
echo "export N3=$N3" >> /etc/profile
echo $N1, $N2, $N3

# 노드 보안그룹에 eksctl-host 에서 노드(파드)에 접속 가능하게 룰(Rule) 추가 설정
NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values=*ng1* --query "SecurityGroups[*].[GroupId]" --output text)
echo "export NGSGID=$NGSGID" >> /etc/profile
aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.100/32

# 워커 노드 SSH 접속 : '-i ~/.ssh/id_rsa' 생략 가능
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i hostname; echo; done
yes
yes
yes

for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i hostname; echo; done

kubectl 명령어를 사용해 특정 가용 영역(ap-northeast-2a, ap-northeast-2b, ap-northeast-2c)에 있는 노드의 IP 주소를 조회하고, 이를 각각 N1, N2, N3변수에 할당합니다. 그 후 조회한 IP 주소를 /etc/profile에 환경 변수로 추가함으로써, 시스템 전반에서 해당 변수를 사용할 수 있게 합니다. 

aws ec2 describe-security-groups 명령어를 사용하여 특정 이름 패턴(*ng1*)에 일치하는 보안 그룹의 ID를 조회하고, aws ec2 authorize-security-group-ingress 명령어를 사용해 해당 보안 그룹에 인그레스(수신) 규칙을 추가합니다. 이 규칙은 특정 CIDR(192.168.1.100/32)에서 오는 모든 트래픽(--protocol '-1')을 허용하여, 예를 들어 관리용 서버 또는 특정 클라이언트에서 노드로의 접근을 가능하게 합니다.

마지막으로, 설정한 환경 변수(N1, N2, N3)를 이용해 각 노드에 SSH로 접속하고, 각 노드의 호스트 이름을 출력하는 반복문을 실행합니다.


AWS VPC CNI (선행 지식편)

https://malwareanalysis.tistory.com/555 악분님의 블로그를 참고하여 AWS VPC CNI에 관한 선수 지식을 쌓습니다. (감사합니다.)

 

pkos 스터디 2주차 2편 - AWS VPC CNI

이 글은 쿠버네티스에서 AWS VPC CNI 동작과정을 설명합니다. 몇 가지 선수지식이 필요합니다. 네트워크 인터페이스 iptables route table arp 프로토콜 AWS VPC 쿠버네티스 AWS VPC CNI란? AWS에서 쿠버네티스

malwareanalysis.tistory.com

 

AWS VPC CNI?

AWS에서 쿠버네티스 설치를 하면, 네트워크 설정과 동작은 AWS 네트워크 환경에 영을 받습니다. 그래서 AWS와 쿠버네티스 사이 적절한 중계자가 필요한데, 그 역할을 수행하는 모듈이 AWS VPC CNI입니다.

 

AWS VPC CNI는 모든 node에 적용되야 하므로 daemonset으로 실행됩니다.

# AWS VCP CNI daemonset 확인
kubectl -n kube-system get ds aws-node

# AWS VCP CNI 버전 확인
kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2

DaemonSet aws-node 정보 확인

kubectl -n kube-system get ds aws-node 명령어는 kube-system 네임스페이스에서 aws-node DaemonSet의 상태를 확인합니다. 

이 DaemonSet은 Amazon EKS 클러스터에서 네트워킹을 담당하는 AWS VPC CNI 플러그인을 각 노드에 자동으로 배포합니다.

출력된 결과에서 DESIRED, CURRENT, READY 등의 컬럼을 통해 aws-node DaemonSet이 원하는 대로 실행되고 있는지 확인할 수 있습니다.

kubectl describe daemonset aws-node --namespace kube-system 명령어의 결과에서 grep Image | cut -d "/" -f 2를 사용하는 것은 사용된 컨테이너 이미지의 이름과 태그를 추출하기 위함입니다. 이를 통해 현재 어떤 버전의 AWS VPC CNI가 사용되고 있는지 확인할 수 있습니다.

 

POD IP할당

POD IP대역

pod IP대역은 pod가 생성될 때 할당되는 IP범입니다. pod IP대역은 쿠버네티스 node IP대역과 동합니다. (보통 다른 CNI(예: calico)는 node IP대역과 pod IP대역이 다르게 설정됩니다)

AWS 네트워크 관점에서 node와 pod를 바라보면 모두 AWS VPC CIDR범위에 있습니다.

 

pod IP 생성 실습

deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        resources:
          limits:
            memory: "32Mi"
            cpu: "10m"
        ports:
        - containerPort: 80
kubectl apply -f deploy.yaml

다음 명령어로 nginx 파드를 배포해줍니다. 

kubectl get po -o wide

다음 명령어를 사용해서 pod의 ip를 확인합니다. 192.168.2.171 이네요

해당 파드가 만들어진 노드(인스턴스)의 Vpc 대역과 일하는 것을 확인할 수 있습니다. 

또한 해당 파드가 만들어진 노드(인스턴스)의 프라이빗 ip 서브넷  대역과 일하는 것을 확인할 수 있습니다. 

 

POD IP할당 과정

GPT4에 저도 물어보았습니다. 
aws에서 kops로 쿠버네티스 클러스터를 설치했어. 설치할 때 aws VPC CNI도 같이 설치했어. pod가 생성되면 각 노드에 있는 aws-node pod가 요청을 받고 IP를 스스로 할당하는거야? 아니면 aws-node pod가 aws API을 호출하여 aws가 IP를 할당하고 aws-node에게 응답하는거야?

 

aws-node Pod는 AWS VPC CNI(컨테이너 네트워크 인터페이스) 플러그인을 실행하는 컨테이너입니. AWS VPC CNI를 사용하는 경우, 쿠버네티스 Pod 네트워킹은 AWS의 Virtual Private Cloud(VPC)의 네트워킹 기능을 직접 활합니다. 

이 과정에서 Pod에 IP 주소를 할당하는 방식은 다음과 같습니다:

1. aws-node Pod의 역할: aws-node Pod 내부에서 실행되는 AWS VPC CNI 플러그인은 Pod 생성 요청을 받을 때 AWS의 Elastic Network Interface(ENI)를 사합니다. 이 플러그인은 각 노드에 ENI를 할하고, 각 ENI에 여러 개의 IP 주소를 할할 수 있습니다.

2. IP 할당 과정: Pod가 생성될 , aws-node Pod는 AWS의 API를 호출하여 ENI에 새로운 IP 주소를 할당하도록 요합니다. 이렇게 할당된 IP 주소는 Kubernetes Pod에 직접 할됩니다. 즉, aws-node Pod는 AWS API를 호출하여 IP 주소를 할받고, 그 결과를 바탕으로 쿠버네티스 클러스터 내의 Pod에 IP 주소를 할합니다.

3. 자동화 및 관리: AWS VPC CNI는 자동화된 프로세스를 통해 이러한 작업을 수합니다. 관리자가 직접 IP 주소를 할당하거나 관리할 필요는 없으며, AWS VPC CNI와 쿠버네티스가 이를 처합니다. 이 방식은 VPC 내에서 자원을 효율적으로 활용하고, AWS의 네트워킹 기능을 최대한 활용하여 고성능 네트워킹을 구현할 수 있게 해줍니다.

요약하자면, aws-node Pod는 AWS API를 호출하여 IP 주소를 할받고, 이 IP 주소는 쿠버네티스 클러스터 내의 Pod에 할됩니다. 이 과정은 전적으로 AWS VPC CNI에 의해 자동으로 관리되며, 쿠버네티스 사용자는 이 과정을 신경 쓸 필요가 없습니다.


네트워크 네임스페이스 분석

AWS VPC CNI는 생성된 pod에 IP를 할당하기 위 네트워크 네임스페이스를 생합니다. 네트워크 네임스페이스에서 조회한 IP는 pod IP와 동합니다.

POD간 통신

네트워크 네임스페이스로 보는 pod와 node

pod가 생성되면 네트워크 네임스페이스가 만들어집니 pod 네트워크 네임스페이스와 root 네임스페이스를 연결하기 위해 가상 인터페이스(veth)를 사합니다.

(coredns처럼 hostIP를 사용하는 일부 pod는 root 네임스페이스를 사합니다)

root 네임스페이스에서 네트워크 인터페이스를 조회하면, pod 네트워크 인터페이스 연결된 가상 인터페이스가 보입니다.

 

 

쿠버네티스 통신을 제어하는 route table

쿠버네티스 네트워크 통신 방향은 route table 영을 받습니다.

pod default gateway는 root 네임스페이스에 있는 가상 네트워크 인터페이스로 빠져나갑니다.

root 네임스페이스로 흘러간 트래픽자연스럽게 root 네임스페이스의 route table영을 받습니다. 트래픽은 route table에 의해 node내부로 갈지 외부로 갈지 결됩니다.

 

이러한 이유로 pod가 생성되면 가상 네트워크 인터페이스와 route table이 추가 됩니다!

 

같은 node의 파드간 통신 디버깅

가상 네트워크 인터페이스는 root 네임스페이에 있으므로 node 라우팅 테이블을 따릅니. 트래픽은 라우팅 테이블에 따라 목적지 pod에 연결된 가상 인터페이스로 흘러갑니다.

 

다른 node의 파드간 통신 디버깅

다른 node간 pod통신도 크게 다르지 않습니다. route table에 의해 다른 node로 트래픽이 전달됩니다. 다른 node로 트래픽이 흘러갈 때, aws route table이 관여하게 됩니다.

 

pod 외부통신

pod가 외부와 통신하면 SNAT(출발지 IP가 node IP로 변경됨)가 됩니. SNAT은 iptables이 수합니다.

Loadbalancer타입 service

동작원리

loadbalancer타입 서비스 생성/수정/삭제는 aws-load-balancer pod가 제합니다. aws-load-balancer는 api-server와 aws API를 사용하여 aws에 NLB(default)를 생합니다

 

pod에 접근하는 2가지 유형

Loadbalancer타입 서비스는 결국 AWS NLB를 사하므로, NLB설정을 그대로 따릅니다. NLB가 pod에 접근하는 유형은 2가지입니다.

- 유형1: node(인스턴스)에 직접 접

NLB에 들어온 요청은 node로 전합니다. node에 들어오는 service트래픽은 iptables에 의해 제됩니다. iptables는 kube-proxy가 설합니다.

 

 

- 유형2: pod IP로 직접 접근

유형1에서는 pod에 접근하기 위해 많은 과정을 거칩니다. 유형2에서는 단순하게 pod에 직접접근하는 방입니다. service를 거치지 않는다고 해서 service가 필요없는건 아닙니다. service에 설정된 endpoint를 NLB가 참조하여 pod로 접하게 됩니다.

 

Loadbalancer타입 서비스와 deployment를 생성해보겠습니다. NLB유형은 IP로 설정했습니다. 유형은 annotations에서 설정할 수 있습니다.

 

IP유형으로 NLB를 생성했기 때문에, NLB는 pod로 직접 접근합니다. pod정보는 target group으로 관리됩니다.


 

AWS VPC CNI

K8S CNI

Container Network Interface 는 k8s 네트워크 환경을 구성해줍니다.

 

AWS VPC CNI

파드의 IP를 할당해준다, 파드의 IP 네트워크 대역과 노드(워커)의 IP 대역이 같아서 직접 통신이 가

- supports native VPC networking with the Amazon VPC Container Network Interface (CNI) plugin for Kubernetes.

- VPC 와 통합 : VPC Flow logs , VPC 라우팅 정책, 보안 그룹(Security group) 을 사용 가능함

- This plugin assigns an IP address from your VPC to each pod.

- VPC ENI 에 미리 할당된 IP(=Local-IPAM Warm IP Pool)를 파드에서 사용할 수 있음

 

 

네트워크 기본 정보 확인

# CNI 정보 확인
kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2

# kube-proxy config 확인 : 모드 iptables 사용 >> ipvs 모드 사용하지 않는 이유???
kubectl describe cm -n kube-system kube-proxy-config

# 노드 IP 확인
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table

# 파드 IP 확인
kubectl get pod -n kube-system -o=custom-columns=NAME:.metadata.name,IP:.status.podIP,STATUS:.status.phase

# 파드 이름 확인
kubectl get pod -A -o name

# 파드 갯수 확인
kubectl get pod -A -o name | wc -l

1. kubectl describe daemonset aws-node --namespace kube-system: 이 명령은 kube-system 네임스페이스에서 실행 중인 aws-node 데몬셋에 대한 자세한 정보를 출력합니다.

2. grep Image: aws-node 데몬셋에 사용되고 있는 이미지의 이름들이 출력됩니다:

- amazon-k8s-cni-init:v1.16.4-eksbuild.2

- amazon-k8s-cni:v1.16.4-eksbuild.2

- amazon

 

kubectl describe cm -n kube-system kube-proxy-config

kubectl describe cm -n kube-system kube-proxy-config 명령어는 kube-system 네임스페이스에 있는 kube-proxy-config ConfigMap에 대한 자세한 정보를 출력합니다. 

이 ConfigMap은 kube-proxy의 구성을 정의하고 있으며, 클러스터의 네트워크 설정과 관련된 많은 옵션들이 포함되어 있습니다. 

 출력에서는 bindAddress, clusterCIDR, conntrack, iptables, ipvs 등의 구성 요소에 대한 정보를 확인할 수 있습니다. 

 

aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table

실행 중인 EC2 인스턴스의 이름, 사설 및 공인 IP 주소, 상태를 확인할 수 있습니다.

 

kubectl get pod -n kube-system -o=custom-columns=NAME:.metadata.name,IP:.status.podIP,STATUS:.status.phase

각 행은 kube-system 네임스페이스에 있는 하나의 파드를 나타내며, 파드의 이름, IP 주소 및 상태를 표시합니다. 

이를 통해 kube-system 네임스페이스에 있는 파드들이 현재 실행 중이며 정상적으로 동작하는지를 확인할 수 있습니다.

 

노드에 네트워크 정보 확인

for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i tree /var/log/aws-routed-eni; echo; done

각 노드에 대해 /var/log/aws-routed-eni 디렉토리 내에 있는 파일들을 출력합니다. 

여기서는 각 노드의 네트워크 관련 로그 파일들이 나열되어 있습니다. 

로그 파일들은 ebpf-sdk.log, egress-v6-plugin.log, ipamd.log, network-policy-agent.log, plugin.log입니다. 

모든 노드에서 동일한 로그 파일들이 존재하며, 해당 로그 파일들은 노드의 네트워크 동작 및 상태에 관한 정보를 포함하고 있습니다.

 

 

ssh ec2-user@$N1 sudo cat /var/log/aws-routed-eni/plugin.log | jq

 

N1에 해당하는 첫 번째 노드의 IP 주소로 SSH 연결을 시도하고 SSH 세션을 통해 해당 노드에 접속하여 /var/log/aws-routed-eni/plugin.log 파일의 내용을 읽어와서 JSON 형식으로 출력한 후, jq를 사용하여 이를 파싱합니다.

각 로그 이벤트는 routed-eni-cni-plugin에서 발생한 것이고, AWS의 네트워크 인터페이스와 관련된 작업을 수행합니다. 예를 새로운 로거 인스턴스의 생성, CNI 추가 요청의 수신 및 응답, 네트워크 설정 등이 이벤트로 포됩니다.

 

for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -br -c addr; echo; done

 

kubectl apply -f deploy.yaml

N1 N2 N3 노드에 대해 반복하여 실행되며, 각 노드에서 sudo ip -br -c addr 명령어를 통해 네트워크 인터페이스의 정보를 출력합니다.

어떤 의미냐 하면, 1번 3번 노드는 ENI 네트워크가 두개라는것을 의미합니다. (프라이빗 ipv4 주소 참조 )

하지만 두번째 노드는 두개를 가지고 있습니 다.  

 

위 아키텍쳐 부분에 해당하는데,  LAN카드 두장에 해당하는 ip는 10개입니다.

192.168.1.218:

- lo: 로컬 루프백 인터페이스, IP 주소가 127.0.0.1/8 및 ::1/128입니다.

- eth0: 메인 네트워크 인터페이스, IP 주소가 192.168.1.218/24입니다.

- eth1: 추가 네트워크 인터페이스, IP 주소가 192.168.1.41/24입니다.

192.168.2.7:

- lo: 로컬 루프백 인터페이스, IP 주소가 127.0.0.1/8 및 ::1/128입니다.

- eth0: 메인 네트워크 인터페이스, IP 주소가 192.168.2.58/24입니다.

192.168.3.145:

- lo: 로컬 루프백 인터페이스, IP 주소가 127.0.0.1/8 및 ::1/128입니다.

- eth0: 메인 네트워크 인터페이스, IP 주소가 192.168.3.236/24입니다.

 

lo eth0 eth1 ?

1. 로컬 루프백 인터페이스 (lo):

- 로컬이라는 말은 컴퓨터 내부에서만 작동한다는 것을 의합니다. 이 인터페이스는 퓨터 자신과의 통신을 담합니다. 다른 컴퓨터나 외부 네트워크와의 통신을 하지 않고, 오직 자신과의 통신만을 담당합니다.

- 루프백은 이 인터페이스가 데이터를 보낸 후에 다시 그 데이터를 받는다는 입니다. 즉, 자기 자신에게 데이터를 보내고 받아 확인할 수 있습니다.

2. 메인 네트워크 인터페이스 (eth0):

- 이것은 컴퓨터가 네트워크에서 다른 기기들과 통신할 때 사용하는 주요한 인터페이입니다. 이 인터페이스를 통해 인터넷이나 다른 컴퓨터와 데이터를 주고받습니다. 가장 기본적인 네트워크 연결을 담당합니다.

3. 추가 네트워크 인터페이스 (eth1):

- 추가적인 네트워크 연결을 위한 인터페이입니다. 일반적으로는 메인 네트워크 인터페이스와는 별도로 설되어 있습니다. 이를 통해 두 번째 네트워크에 연결하거나, 가상 네트워크 등 다른 네트워크에 연결할 수 있습니다.

요약하자면, 로컬 루프백 인터페이스는 컴퓨터 자신과의 통신을 담당하고, 메인 네트워크 인터페이스는 외부와의 통신을 담당하며, 추가 네트워크 인터페이스는 보조적인 네트워크 연결을 담당합니다.

 

for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c addr; echo; done

위 명령어는 각 노드의 네트워크 인터페이스 정보를 더 자세하게 조회합니다. 

여기에는 루프백 인터페이스와 이더넷 인터페이스의 상태와 IP 주소 정보가 포함됩니다.

 

for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c route; echo; done

위 명령어는 각 노드에서 라우팅 테이블을 조회하여 출력합니다. 여기서 각 줄은 다음과 같은 정보를 제공합니다:

- default via [게이트웨이 주소] dev [인터페이스]: 기본 라우팅 규칙입니다. 즉, 해당 노드에서 목적지 IP 주소로 패킷을 보낼 때 사용하는 기본 게이트웨이와 인터페이스를 제공합니다.

- 169.254.169.254 dev [인터페이스]: 메타데이터 주소에 대한 라우팅 규칙입니다. 일반적으로 AWS EC2 인스턴스에서 사용됩니다.

- [네트워크 주소]/[넷마스크 길이] dev [인터페이스] proto kernel scope link src [소스 주소]: 노드에서 직접 연결된 네트워크에 대한 라우팅 규칙입니다. 이것은 네트워크 인터페이스에 할당된 IP 주소와 관련이 있습니다.

- [목적지 주소] dev [인터페이스] scope link: 특정 목적지 주소에 대한 직접 연결된 라우팅 규칙입니다. 일반적으로 가상 머신 간의 통신에 사용됩니다

 

gpt한테 중학생도 알아들을 수 있을 정도로 쉽게 설명해달라고 했습니다. 

각 노드에서 데이터를 보낼 때 어디로 보낼지에 대한 "지도"를 보여줍니다. 이 지도를 "라우팅 테이"이라고 합니다.

- "default via [게이트웨이 주소] dev [인터페이스]": 어떤 곳으로 보낼지 모를 때 사용하는 기본적인 길이에 대한 정입니다. 즉, 노드가 모르는 곳으로 데이터를 보낼 때 사용하는 주소와 방을 알려줍니다.

- "169.254.169.254 dev [인터페이스]": 이 주소는 특별한 용도로 예약되어 있습니다. 주로 AWS와 같은 클라우드 환경에서 사용됩니다.

- "[네트워크 주소]/[넷마스크 길이] dev [인터페이스] proto kernel scope link src [소스 주소]": 이 부분은 노드가 직접 연결되어 있는 네트워크에 대한 정보입니다. 즉, 노드가 직접 사용하는 네트워크에 대한 정보를 제공합니다.

- "[목적지 주소] dev [인터페이스] scope link": 이 부분은 특정한 목적지 주소에 대한 정보입니다. 예를 들어, 가상 머신 사이의 통신에 사용됩니다.

 

ssh ec2-user@$N1 sudo iptables -t nat -S

 

첫 번째 노드(N1)의 iptables NAT(네트워크 주소 변환) 설정을 보여줍니다. 

NAT 테이블은 네트워크 패킷이 라우터를 통과할 때 출발지 IP 주소 또는 도착지 IP 주소를 변경하는 데 사용됩니다.

 

 

ssh ec2-user@$N1 sudo iptables -t nat -L -n -v

NAT(네트워크 주소 변환) 테이블의 설정을 보여줍니다

명령은 몇 개의 체인(chain)에 대한 정보를 보여줍니다:

1. PREROUTING: 패킷이 라우터로 들어오기 전에 처리되는 규칙을 보여줍니다. 여기에는 KUBE-SERVICES와 AWS-CONNMARK-CHAIN-0 등의 규칙이 있습니다.

2. INPUT: 들어오는 패킷을 처리하는 규칙을 보여줍니다. 여기에는 규칙이 없습니다.

3. OUTPUT: 나가는 패킷을 처리하는 규칙을 보여줍니다. 여기에는 KUBE-SERVICES 규칙이 있습니다.

4. POSTROUTING: 라우터를 떠나는 패킷을 처리하는 규칙을 보여줍니다. 여기에는 KUBE-POSTROUTING와 AWS-SNAT-CHAIN-0 등의 규칙이 있습니다


노드에서 기본 네트워크 정보 확인

노드1의 기본 네트워크 구성

- Network 네임스페이 호스트(Root) 파드 별(Per Pod)로 구분된다

- 특정한 파드(kube-proxy, aws-node)는 호스트(Root)의 IP를 그대로 사한다 ⇒ 파드의 Host Network 옵션

[Kubernetes] Pod 관련 Host Network 옵션과 동작원리

- t3.medium 의 경우 ENI 마다 최대 6개의 IP를 가질 수 있다

- ENI0, ENI1 으로 2개의 ENI는 자신의 IP 이외에 추가적으로 5개의 보조 프라이빗 IP를 가질수 있다

- coredns 파드는 veth 으로 호스트에는 eniY@ifN 인터페이스와 파드에 eth0 과 연결되어 있다

 

1. Per Pod Net Namespace:

- '파드'이라는 컨테이너 그룹 각각에 대한 네트워크 공간을 보여줍니다.

- 각 파드는 자기만의 고유한 IP 주소를 가지고 있고, 이 예시에서는 coredns라는 컨테이너에 192.168.1.235라는 IP 주소가 할당되어 있습니다.

2. Root Net Namespace:

- 이건 모든 파드와 컨테이너가 사용하는 주 네트워크 공간입니다. 

- 여기서는 두 개의 주요 컴포넌트 aws-node kube-proxy가 있는데, 같은 IP 주소 192.168.1.64를 공유하고 있어. 이건 그들이 기본적인 네트워크 통신을 위해 사용하는 주소입니다.

그리고 ENI (Elastic Network Interface)는 AWS에서 제공하는 네트워크 카드 같은 것으로, EC2 인스턴스(가상 서버)에 물리적인 네트워크 연결을 제공합니다. 이 인터페이스를 통해 팟과 컨테이너들이 인터넷이나 다른 서비스와 통신할 수 있습니다. 

ENI0 ENI1은 이 가상 네트워크 카드들의 이름이고, 각각 고유한 IP 주소를 가지고 있습니

그리고 아래쪽에 보이는 'Secondary private IPv4 addresses'는 각 ENI가 가질 수 있는 추가적인 IP 주소들입니다.

 하나의 네트워크 카드에 여러 개의 전화번호를 가진 핸드폰처럼, 하나의 ENI가 여러 개의 IP 주소를 가질 수 있게 해줍니다.

 

더 종합적인 설명

네트워크 네임스페이스는 컴퓨터 네트워크에서 각각의 그룹이나 구역을 의미합니다. 

이 그림에서는, 호스트 (전체 시스템을 의미하는 'Root') 각각의 파드 (쿠버네티스에서 컨테이너의 그룹을 의미하는 'Per Pod') 네트워가 나타나고 있어요.

특정 파드 (예를 들어 kube-proxy, aws-node)는 호스트의 네트워크 설정을 그대로 사용해서, 마치 호스트의 일부처럼 작하죠. 이것을 'Host Network 옵'이라고 하며, 파드가 호스트의 IP 주소와 포트를 공하게 됩니다.

t3.medium 인스턴스처럼, 각 네트워크 인터페이스 (ENI)는 주 IP 주소 외에 여러 개의 보조 프라이빗 IP 주소를 가질 수 있어. 이 경우 ENI0와 ENI1은 각각 5개의 추가 IP를 가질 수 있습니다.

coredns 파드는 네트워크 상에서 호스트의 eniY@ifN 인터페이 파드 안의 eth0 인터페이스로 연되어 있어서, 둘 사이의 통신을 가능하게 해줍니다.

그리고, hostNetwork 옵션을 true로 설정하면 파드가 배포된 노드의 IP 주소를 직접 사용하게 됩니다. 이는 파드가 노드의 네트워크를 직접 사용하기 때문에, containerPort hostPort를 동일하게 설정해야 합니다. 그러나 보안상의 이유로, 쿠버네티스는 이러한 설정을 꼭 필요한 경우가 아니면 사용하지 않도록 권장합니다​​.


워커 노드1 인스턴스의 네트워크 정보 확인 : 프라이빗 IP와 보조 프라이빗 IP 확인

보조 IPv4 주소를 파드가 사용하는지 확인

# coredns 파드 IP 정보 확인
kubectl get pod -n kube-system -l k8s-app=kube-dns -owide

# 노드의 라우팅 정보 확인 >> EC2 네트워크 정보의 '보조 프라이빗 IPv4 주소'와 비교해보자
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c route; echo; done

kubectl get pod -n kube-system -l k8s-app=kube-dns -owide
kubectl get node -owide

coredns와 Ip가 다르다. 

 

이 네트워크 네임 스페이스가 달라서 일어나는 일이다.

 

kubectl get pod -A -owide

반면 다음 명령어로 확인한 Kube proxy와 node는 coredns와 Ip가 같은것을 알 수 있는데 

루트 네임 스페이스를 그대로 사용하기 때문이다. 

kubectl get pod -n kube-system -l k8s-app=kube-dns -owide

"kube-system" 네임스페이스에 있는 "kube-dns" 앱 레이블이 붙은 파드들의 상세한 정보를 보여줍니다. 

파드 이름, 상태, 재시작 횟수, 나이(얼마나 오래되었는지), 파드의 IP 주소, 그리고 파드가 어떤 노드 위에서 돌아가고 있는지 등이 포함됩니다.

 

for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c route; echo; done

각 노드의 IP 경로를 확인합니다. ssh 명령어는 원격 노드에 로그인해서, ip -c route를 통해 각 노드의 네트워크 경로 설정을 출력해줍니다. 이 정보는 트래픽이 어디로 라우팅 되는지, 그리고 특정 IP 주소로 향하는 특정 경로들을 보여줍니다. 예를 들어, 노드의 메인 IP와 파드 IP가 어떻게 연결되어 있는지를 볼 수 있습니다.

 

ssh ec2-user@$N1
watch -d "ip link | egrep 'eth|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"

ssh ec2-user@$N2
watch -d "ip link | egrep 'eth|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"

ssh ec2-user@$N3
watch -d "ip link | egrep 'eth|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"

2초마다 eth0에 어떤 변화가 있는지 watch로 감시합니다.

 

터미널 3개를 사용해서 각 터미널마다 각각의 파드를 감시합니다. 

 

테스트용 파드 netshoot-pod 생성

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

쿠버네티스 클러스터에 새로운 배포(Deployment)를 만듭니다.

netshoot-pod라는 이름의 배포를 만들어서, nicolaka/netshoot이라는 도커 이미지를 사용하는 세 개의 파드(replicas)를 클러스터에 만듭니다.  파드들은 데이터를 전송하거나 네트워크 문제를 진단할 때 유용한 다양한 네트워크 도구들을 가지고 있죠. 여기서 사용된 tail -f /dev/null 명령어는 파드가 계속 실행되도록 해서, 네트워크 연결 상태를 확인하거나 다른 진단 작업을 할 때 유용하게 사용할 수 있게 해줍니다.

 

# 파드 이름 변수 지정
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})

# 파드 확인
kubectl get pod -o wide
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP

# 노드에 라우팅 정보 확인
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c route; echo; done

netshoot-pod 라벨이 붙은 파드들의 이름을 저장하기 위해 변수 PODNAME1, PODNAME2, PODNAME3을 설정했고, 이 변수들은 각각 첫 번째, 두 번째, 세 번째 netshoot 파드의 이름을 저장합니다.

kubectl get pod -o wide 명령을 사용해서 모든 파드의 이름과 IP 주소를 확인했습니다. 여기서 볼 수 있듯이 각각의 netshoot 파드는 워커 노드에 하나씩 배포된 것을 확인할 수 있습니다. 

for 루프를 이용하여 각 노드에 SSH로 접속해서, 노드의 네트워크 경로를 확인합니다.

이 경로는 파드가 어떤 노드에 위치하고 있는지, 그리고 각 노드의 네트워크 트래픽이 어떻게 이동하는지를 보여줍니다. 예를 들어, 192.168.1.63 IP를 가진 파드는 192.168.1.106 노드에 있고, 그 노드의 네트워크 경로 설정을 볼 수 있죠.

 

NETSHOOT POD의 

192.168.1.64 ip를 가진 파드는 첫번째 노드의 라우팅 테이블 세번째 줄을 보면 알 수 있듯이 추가되어있다. 

192.168.2.52 ip를 가진 파드는 두번째 노드의 라우팅 테이블 세번째 줄을 보면 알 수 있듯이 추가되어있다. 

192.168.3.206 ip를 가진 파드는 번째 노드의 라우팅 테이블 네번째 줄을 보면 알 수 있듯이 추가되어있다. 

ssh ec2-user@$N3
----------------
ip -br -c addr show
ip -c link
ip -c addr
ip route # 혹은 route -n

EC2 인스턴스에 원격으로 접속하기 위해 사용되었습니다. 

 

인스턴스의 네트워크 인터페이스와 연결 상태를 확인합니다. 여기에는 IP 주소, MAC 주소와 같은 네트워크 정보가 나타납니다.

 

인스턴스에 할당된 모든 IP 주소들을 색상 코드와 함께 보여줍니다. 'lo'는 로컬호스트 주소, 'eth0', 'eth1'은 외부 통신을 위한 인터페이스를 나타냅니다.

 

인스턴스의 네트워크 라우팅 테이블을 보여줍니다. 이 테이블은 데이터 패킷이 어떻게 인스턴스 내부와 외부로 이동하는지에 대한 경로를 나타냅니다.

 

# 마지막 생성된 네임스페이스 정보 출력 -t net(네트워크 타입)
sudo lsns -o PID,COMMAND -t net | awk 'NR>2 {print $1}' | tail -n 1

# 마지막 생성된 네임스페이스 net PID 정보 출력 -t net(네트워크 타입)를 변수 지정
MyPID=$(sudo lsns -o PID,COMMAND -t net | awk 'NR>2 {print $1}' | tail -n 1)

# PID 정보로 파드 정보 확인
sudo nsenter -t $MyPID -n ip -c addr
sudo nsenter -t $MyPID -n ip -c route

네트워크 네임스페이스의 프로세스 ID를 찾습니다.

 

가장 최근의 프로세스 ID를 변수 MyPID에 저장합니다. 

sudo nsenter 명령어를 사용하여 해당 프로세스의 네트워크 네임스페이스에 접근하고, 그 안에서 ip -c addr ip -c route 명령어를 실행하여 IP 주소와 라우팅 정보를 확인합니다.

 

테스트용 파드 접속

netShoort을 실행한 뒤 두번째 노드에 라우팅 테이블이 하나 추가되었습니다.

7: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP mode DEFAULT gr
oup default qlen 1000
    link/ether 06:55:7c:71:88:8d brd ff:ff:ff:ff:ff:ff

다음과 같은 로그가 확인되는데 LAN카드가 하나 더 추가되었음을 확인할 수 있다. 

AWS 콘솔에서 확인해보면 LAN카드가 하나 더 꼽힌것을 확인할 수 있고 (프라이빗 IPv4주소)

보조 프라이빗 IPv4 주소도 10개로 늘어난 것을 확인할 수 있다. 

 

6: eni9fe60197b05@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state
 UP mode DEFAULT group default
    link/ether a6:b1:fb:84:f0:2e brd ff:ff:ff:ff:ff:ff link-netns cni-f331f1c5-74b0-5
e13-94f9-3b1e9d74be17

veth Interface가 하나 더 추가된것을 위 로그로 확인할 수 있다. 

'veth Interface'라는 건 가상 네트워크 카드인데, 컴퓨터 안에서 마치 진짜 네트워크 카드처럼 행동해서, 컨테이너들이 서로 통신할 수 있게 도와준다. 

 

노드3에서 네트워크 인터페이스 정보 확인

ip -br -c addr show
ip -c link
ip -c addr
ip route # 혹은 route -n

ip -br -c addr show 명령을 사용해서 확인한 로그에서는 

 

LAN카드 두장

- eth0 192.168.3.145/24 fe80::893:8cff:fe16:1d65/64

- eth1 192.168.3.155/24 fe80::8f5:8ff:fed0:af8f/64

veth interface 2개

- enieaf52763b26@if3 UP fe80::aa:dbff:feea:fbe2/64

- eniff47b5729c7@if3 UP fe80::14c5:3bff:fe0a:1042/64

 

# 마지막 생성된 네임스페이스 net PID 정보 출력 -t net(네트워크 타입)를 변수 지정
MyPID=$(sudo lsns -o PID,COMMAND -t net | awk 'NR>2 {print $1}' | tail -n 1)

# PID 정보로 파드 정보 확인
sudo nsenter -t $MyPID -n ip -c addr
sudo nsenter -t $MyPID -n ip -c route

netshoot 파드의 격리된 네트워크만 본 것 . 192.168.3.206

 

위의 @If3은 밑의 : eth0@if5 과 연결되어있다. 

호스트의 @if3 Veth interface는 컨테이너 내부의  eth0@if5 인터페이스랑 연결되어있다.

 

위 부분을 설명한 것이다 . 파드에서 'eth0' 인터페이스가 호스트 네임스페이스의 'eniY@ifN'이라는 다른 네트워크 인터페이스에 연결되어있고 두개는 네트워크 스페이스가 다르다는 설명을 한 것이다. 

 

테스트용 파드 접속 

 ip -c addr

netshoot안에서 Ip를 확인해보았다. 

192.168.1.64

 

kubectl get pod -owide

노드에서 위 명령을 하면 파드의 Ip를 확인할 수 있다. 

 

ping -c 1 192.168.3.206

잘 통신 되는것을 확인할 수 있다. 


노드 간 파드 통신 

다른 워커 노드간 통신이다.

ssh ec2-user@$N1
ssh ec2-user@$N2
ssh ec2-user@$N3

1,2,3번 터미널마다 각 워커 노드로 접속을 해준다. 

 

sudo tcpdump -i any -nn icmp

'sudo tcpdump -i any -nn icmp' 명령어는 네트워크를 통해 어떤 정보가 오가는지를 관찰하는 도구인데, 여기서는 특별히 'icmp'라는 종류의 신호만 살펴보라고 컴퓨터에 요청합니다. 

'icmp'는 인터넷에서 다른 컴퓨터가 잘 작동하고 있는지 확인할 때 사용하는 신호입니다. (예를 들어, 인터넷에 '핑'을 보낼 때 사용)

'-i any'는 모든 네트워크 카드를 통해 오가는 정보를 본다는 뜻.

 

kubectl exec -it $PODNAME1 -- ping -c 2 $PODIP2

파드 1번에서 파드 2번으로 ping을 보냈더니 송 수신 내역이 tcpdump로 캡쳐가 된 모습이다.

인캡슐레이션 & 터널링 된 정보가 없다. 

원본 패킷이 목적지에서도 그대로 확인이 되고있다. -> 터널링을 하지 않는다 .

 

터널링을 하지않는것의 장점 

(왼쪽 사진) 온프렘 CNI들은 워커노드간 유연한 통신을 하기 위해서 워커노드를 빠져나갈때 터널링을 많이 사용함 

(오른쪽 사진) 반면 VPC CNI는 원본 그대로 통신하고 네이티브로 라우팅을 처리한다. 

-> 오버헤드 없이 통신이 가능하다 .

 


파드에서 외부 통신

제일 위 bastion host터미널의 띄워두고

밑에는 각각의 워커노드 쉘에 접속하여 tcpdump를 셋팅한다. 

kubectl exec -it $PODNAME1 -- ping -c 1 www.google.com

 

첫번째 쉘에서 google에 ping을 쏘도록 명령합니다. 

제일 왼쪽에 터미널에서 패킷 이동이 캡쳐된것을 확인할 수 있습니다. 

 

kubectl exec -it $PODNAME1 -- ping -i 0.1 www.google.com

더 확실하게 확인하기 위해서 다음 명령을 사용해서 google에 ping을 보냅니다. 

 

확실히 외부랑 통신되고 있는 것을 확인할 수 있다.

 

for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i curl -s ipinfo.io/ip; echo; echo; done

다음 명령을 사용해서 각각의 노드가 외부 통신에 사용하는 공인 ip를 확인한다. 

 

AWS 콘솔에서 확인한 한 노드의 퍼블릭 주소랑 일치하는 것을 확인할 수 있다. 

for i in $PODNAME1 $PODNAME2 $PODNAME3; do echo ">> Pod : $i <<"; kubectl exec -it $i -- curl -s ipinfo.io/ip; echo; echo; done

파드 내에서 curl 요청을 했을때 확인한 공인 Ip랑 , 워커노드에서 외부 접근했을때의 외부 ip랑 같습니다.

파드가 외부랑 통신할때는 해당하는 워커노드의 이더넷 0번에 매핑되어있는 유동 공인아이피로 소스 Nat가 됩니다. 

 

더 쉽게

파드들은 자기 자신의 고유한 내부 IP를 가지고 있지만, 인터넷과 대화할 때는 그 정보를 담은 메시지들을 '워커 노드'라고 불리는 컴퓨터를 통해 보냅니다.

워커 노드는 여러 파드의 메시지를 모아서 자기의 공인 IP(즉, 인터넷에 보이는 주소)를 사용해 전송하고, 이 과정에서 워커 노드의 IP가 '소스 NAT'로 사용됩니다. 외부에서 보면 마치 워커 노드가 직접 메시지를 보낸 것처럼 보입니다. 이렇게 하면 많은 파드들이 같은 공인 IP를 공유해서 인터넷과 통신할 수 있습니다.

 

kubectl exec -it $PODNAME1 -- curl -s wttr.in/seoul

외부로 통신이 되니까 이런 날씨 출력도 된다고한다. 귀욤

watch -d 'sudo iptables -v --numeric --table nat --list AWS-SNAT-CHAIN-0; echo ; sudo iptables -v --numeric --table nat --list KUBE-POSTROUTING; echo ; sudo iptables -v --numeric --table nat --list POSTROUTING'

 

kubectl exec -it $PODNAME1 -- ping -i 0.1 www.google.com

구글에 핑을 엄청 쏘는 명령을 하면 

 

snat chain이라고 되어있는 포스트 라우팅이 파드가 외부랑 통신하는  ip table rule에 snat (마스커레이딩) 을 통해 워커 노드의 첫번째 네트워크 인터페이스의 아이피로 source nat되고, 그 Ip가 유동 공인 ip로 매핑이 되어서 외부로 나갔다가 들어온다. 그것이 Ip table rule로 셋팅이 되어있다. 그리고 이 ip table rule을 관리하는게 kube proxy이다. 

 

쉬운 설명 

여기서 한 일은 쿠버네티스 안의 파드가 외부 네트워크, 예를 들면 구글에 '핑'을 보내는 걸 관찰하는것입니다. 

이 '핑'을 보낼 때, 파드는 실제로 워커 노드의 IP 주소를 사용해서 인터넷에 나가고, 이 과정을 '소스 NAT' 또는 '마스커레이딩'이라고 합니다.

마치 학교에서 이름 대신 학번을 사용하는 것처럼, 파드는 자기 진짜 IP 대신 워커 노드의 IP를 빌려서 구글에 '핑'을 보내는 것이다.

그리고 'iptables'라는 컴퓨터의 규칙표에 이 모든 정보가 적혀있어서, 컴퓨터는 어디로 데이터를 보내고 받아야 하는지 알 수 있는데,이 규칙표를 관리하는 건 'kube-proxy'입니다. 


 노드에 파드 생성 갯수 제한 

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"}}'

위 명령들을 사용해서 작업용 ec2에서 실행합니다

 

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"}'

위 명령을 하면 접근할 수 있는 Url이 나옵니다. 1.5배를 추천하신다고합니다.

 

 

kubectl get pod -A

실제로도 파드는 9개가 떠있습니다.

 

 kubectl get pod -A -owide

더 자세하게 보면 ip쪽에서 확인할 수 있듯이 각각의 노드에서 3개씩 떠있는 것을 확인 가능합니다.

 

해당 주소는 클래식 로드밸런서의 DNS 주소이다. 

 

현재 T3 미디엄을 워커노드로 사용하고 있고, 이에 할당 가능한 ip의 최대 갯수는 15개입니다.

aws ec2 describe-instance-types --filters Name=instance-type,Values=t3.* \
 --query "InstanceTypes[].{Type: InstanceType, MaxENI: NetworkInfo.MaximumNetworkInterfaces, IPv4addr: NetworkInfo.Ipv4AddressesPerInterface}" \
 --output table

다음 명령어로도 확인이 가능합니다.

 

# 파드 사용 가능 계산 예시 : aws-node 와 kube-proxy 파드는 host-networking 사용으로 IP 2개 남음
((MaxENI * (IPv4addr-1)) + 2)
t3.medium 경우 : ((3 * (6 - 1) + 2 ) = 17개 >> aws-node 와 kube-proxy 2개 제외하면 15개

계산식의 경우는 다음과 같습니다.

ipvaddr에서 하나를 빼는건 자기 자신을 빼야해서 그렇다. 뒤에 2의 경우는 kube proxy와 aws node demon이 host network name space를 공유하니까 그 값이다.  

kubectl describe node | grep Allocatable: -A6

노드의 정보를 describe 한 것인데, 최대 파드 17개가 allowcation 가능하다는것을 확인 가능합니다.

 

위 터미널은 작업용 노드, 그리고 밑은 워커노드 쉘을 띄워둡니다.

 

while true; do ip -br -c addr show && echo "--------------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done

워커 노드 쉘을 하나 더 띄워서 위 명령어를 입력해 줍니다.

해당 명령어는 네트워크 상태를 매 초마다 체크하고, 그 상태와 현재 시간을 화면에 보여줍니다. ip -br -c addr show는 네트워크 인터페이스와 IP 주소를 보여주고, date "+%Y-%m-%d %H:%M:%S"는 현재 날짜와 시간을 보여줍니다.

 

curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/2/nginx-dp.yaml
kubectl apply -f nginx-dp.yaml

다음 명령으로 nignx deployment를 위한 yaml을 다운로드 받고, 쿠버네티스 환경에 배포를 적용합니다. 

그러면 Ops view에서 파드 두개가 위에 박힌것을 확인할 수 있습니다. 

더 자세히 확인하면 지금의 deployment로 생성된 nginx 파드입니다. 

위에 있는 파드들과, 밑의 있는 파드들의 차이가 뭐냐하면, 밑에있는 것들은 kube system namespace에 속해있는 것들이고, 그 외의 나머지 네임 스페이스에 속하는것들은 위에 생성됩니다.

 

kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP

파드의 ip정보를 확인해보면 다음과 같이 나오는데, 위 ops view에서 확인할 수 있듯이 해당 Ip에 파드들이 생긴것을 확인 가능합니다.

 

kubectl scale deployment nginx-deployment --replicas=8

디플로이먼트의 레플리카 갯수를 8개로 늘리는 명령 후 Nginx의 파드 갯수가 8개로 늘어난 것을 확인 할 수 있습니다.

 

워커 노드에서 eni(veth interface)가 추가된것을 확인할 수 있습니다.

 

kubectl scale deployment nginx-deployment --replicas=15

디플로이먼트의 레플리카 갯수를 15개로 늘리는 명령 후 워커노드에서 LAN카드가 세개가 된 것을 확인할 수 있습니다.

 

aws 콘솔에서 해당하는 워커 노드의 정보를 ec2에서 확인하면 랜카드가 세개가 들어가있는것을 확인 가능합니다

 

kubectl scale deployment nginx-deployment --replicas=50

디플로이먼트의 레플리카 갯수를 15개로 늘리는 명령 후 모두 할당이 안되고, 오른쪽에 펜딩상태로 몇개가 존재하는것을 확인 가능합니다. 

kubectl delete deploy nginx-deployment

nginx deployment를 삭제 후 ops view를 통해서 확인해보면 nginx 파드들이 다 사라진것을 확인할 수 있다. 

 

AWS 콘솔에서 작업 노드를 확인해보면 LAN카드가 3개에서 두개로 줄어든 것을 확인할 수 있습니다.


Service & AWS LoadBalancer Controller

AWS LoadBalancer Controller 배포

aws eks describe-cluster --name $CLUSTER_NAME --query "cluster.identity.oidc.issuer" --output text
aws iam list-open-id-connect-providers | jq

1. aws eks describe-cluster --name $CLUSTER_NAME --query "cluster.identity.oidc.issuer" --output text

이 명령은 AWS의 EKS (Elastic Kubernetes Service)라는 서비스를 사용할 때, 특정 쿠버네티스 클러스터의 OIDC(OpenID Connect) 발급자 URL을 가져오기 위해 사용됩니다. 즉, 지정한 EKS 클러스터에 대한 OIDC 인증 정보 중에서 '발급자(issuer)'의 URL을 알려주는 것입니다.

 

2. aws iam list-open-id-connect-providers | jq

이 명령은 AWS의 IAM (Identity and Access Management) 서비스에서 설정된 모든 OIDC(OpenID Connect) 공급자 목록을 보여줍니다. jq는 결과를 좀 더 읽기 쉽게 만드는 도구인데, 이 명령을 통해 너의 AWS 계정에 등록된 모든 OIDC 공급자들이 무엇인지 확인할 수 있습니다.

간단히 말해서, 첫 번째 명령어는 특정 EKS 클러스터의 OIDC 발급자 정보를 가져오고, 두 번째 명령어는 네 AWS 계정에 등록된 모든 OIDC 공급자를 나열해주는 겁니다.

 

OIDC가 뭔지 gpt한테 중학생도 알아들을 수 있는 설명을 해달라고 물어보았습니다.

OIDC, 즉 OpenID Connect는 인터넷에서 사람들이 다른 웹사이트나 앱에 로그인할 때 사용할 수 있는 방법 중 하나야. 좀 더 쉽게 설명하자면, 네가 페이스북이나 구글 같은 서비스로 다른 웹사이트에 '로그인하기' 버튼을 누를 때, 그 백그라운드에서 작동하는 기술이야.

이런 식으로, 네가 이미 사용하고 있는 서비스의 계정을 이용해서 다른 곳에 로그인할 수 있게 해주는 거지. 이 과정에서 너의 비밀번호 같은 중요한 정보는 공유되지 않고, 그 서비스만이 너를 인식할 수 있는 정보를 받게 돼. 이게 바로 OIDC가 해주는 일이야.

즉, OIDC는 너와 웹사이트 사이에서 신분을 확인해주는 일종의 '신분증' 역할을 하는 거라고 할 수 있어. 그래서 네가 매번 새로운 웹사이트에서 계정을 만들고 비밀번호를 설정할 필요 없이, 빠르고 안전하게 로그인할 수 있게 도와줘.

 

그리고 출력된 OIDC는 해당 EKS클러스터의 콘솔화면에서 똑같은 URL로 확인할 수 있습니다.

 

IAM Policy (AWSLoadBalancerControllerIAMPolicy) 생성

로드밸런서 컨트롤러는 실제로는 파드이니, AWS상에서 뭔가를 생성하려고한다면 AWS상에서의 권한이 필요합니다. 그래서 IAM 롤을 생성합니다.

 

curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.5.4/docs/install/iam_policy.json
aws iam create-policy --policy-name AWSLoadBalancerControllerIAMPolicy --policy-document file://iam_policy.json

다운로드한 IAM 정책 파일을 사용해 AWS에서  AWSLoadBalancerControllerIAMPolicy라는 이름의 새 정책이 만들어지고, 해당 정책에는 앞서 다운로드한 iam_policy.json 파일에 정의된 권한이 포함되어 있습니다 이 정책은 AWS 로드 밸런서 컨트롤러가 AWS 리소스를 관리하고, 로드 밸런싱 관련 작업을 수행할 수 있게 해주는 권한을 제공합니다.

간단히 말해서, 이 두 명령을 통해 AWS 로드 밸런서 컨트롤러가 작동하는 데 필요한 권한을 정의하고, 그 권한을 AWS IAM 정책으로 생성하는 작업을 합니다.

 

AWS IAM 콘솔에서 확인해보면 AWSLoadBalancerControllerIAMPolicy 이름의 정책이 생성된것을 확인할 수 있습니다.

그리고 AWS 로드밸런서 파드는 해당 정책에 쓰여있는 권한대로 실행할 수 있어야합니다.

 

생성된 IAM Policy Arn 확인

aws iam list-policies --scope Local | jq
aws iam get-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy | jq
aws iam get-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --query 'Policy.Arn'

"AWSLoadBalancerControllerIAMPolicy" IAM 정책의 ARN만을 조회해. --query 옵션을 사용해 출력에서 특정 JSON 키('Policy.Arn')의 값만을 추출해 보여줍니다. 

 

eksctl create iamserviceaccount --cluster=$CLUSTER_NAME --namespace=kube-system --name=aws-load-balancer-controller --role-name AmazonEKSLoadBalancerControllerRole \
--attach-policy-arn=arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --override-existing-serviceaccounts --approve

Kubernetes의 kube-system 네임스페이스에 aws-load-balancer-controller라는 이름의 서비스 계정이 생성되었고, 이 계정은 AWS에서 로드 밸런서 컨트롤러가 작동하는 데 필요한 권한을 가진 IAM 역할에 연결되어 있습니다. 이렇게 설정하면 AWS 로드 밸런서 컨트롤러가 EKS 클러스터 내에서 AWS 리소스를 관리할 수 있게 됩니다.

CloudFormation 스택 메시지는 AWS에서 eksctl을 사용해 EKS 클러스터에 대한 IAM 서비스 계정을 생성하는 과정이 성공적으로 완료되었는것을 확인 가능합니다.

eksctl get iamserviceaccount --cluster $CLUSTER_NAME

AWS EKS(Elastic Kubernetes Service) 클러스터에 연결된 IAM 서비스 계정(IAM Service Account)의 목록을 조회하는 데 사용됩니다.

IRSA는 "IAM Roles for Service Accounts"의 약자로, AWS에서 제공하는 기능 중 하나입니다.

이 기능을 사용하면 Kubernetes 서비스 계정을 AWS IAM 역할에 연결할 수 있습니다.

 간단히 말해서, Kubernetes 내의 특정 서비스(예: 애플리케이션, 컨트롤러 등)가 AWS 리소스에 접근할 때 필요한 권한을 안전하게 관리할 수 있는 방법을 제공합니다.

IRSA를 사용하는 주된 이유는 보안을 강화하기 위해서입니다.

기존에는 AWS 리소스에 접근하기 위해 애플리케이션 코드 내에 AWS의 접근 키와 비밀 키를 직접 저장하는 경우가 많았는데, 이 방법은 키가 노출될 위험이 있고, 키 관리도 복잡했습니다.

IRSA를 사용하면, Kubernetes 서비스 계정에 AWS IAM 역할을 연결함으로써, 애플리케이션이 필요한 AWS 리소스에 접근할 수 있게 되는데, 이 과정에서 AWS의 접근 키와 비밀 키를 직접 다룰 필요가 없어지고, EKS 클러스터와 AWS 간의 신뢰 관계를 통해 권한이 부여되기 때문에 보안이 훨씬 강화되고 관리도 편리해집니다.

 

서비스 어카운트 확인

kubectl get serviceaccounts -n kube-system aws-load-balancer-controller -o yaml | yh

이 서비스 계정에는 eks.amazonaws.com/role-arn 어노테이션이 포함되어 있습니다, 이는 해당 서비스 계정이 AWS IAM 역할에 연결되어 있음을 나타내고, 이 역할을 통해 AWS Load Balancer Controller는 AWS 리소스에 접근할 수 있습니다.

 

AWS 로드밸런서 컨트롤러 helm차트를 사용해서 배포 

# Helm Chart 설치
helm repo add eks https://aws.github.io/eks-charts
helm repo update
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

Helm을 사용해 kube-system 네임스페이스에 AWS Load Balancer Controller를 설치했습니다.

이 과정을 통해 AWS Load Balancer Controller가 네 EKS 클러스터에서 실행되도록 설정되었습니다. 이 컨트롤러는 Kubernetes 서비스를 AWS의 로드 밸런서(예: ALB, NLB)와 자동으로 연동해, 트래픽 관리와 분산을 개선하는 역할을 합니다.

 

AWS 로드밸런서 컨트롤러 설치 확인 

kubectl get crd

 

Kubernetes 클러스터에 현재 설치되어 있는 모든 CRD(Custom Resource Definitions)의 목록을 보여줍니다.

CRD는 Kubernetes API를 확장하는 데 사용되며, 사용자 정의 리소스를 생성하여 Kubernetes의 기능을 확장할 수 있게 합니다.

 

시간을 보면 방금전에 추가된 두개가 있는데

ingressclassparams.elbv2.k8s.aws: AWS Load Balancer Controller를 사용하여 ALB(Application Load Balancer) 또는 NLB(Network Load Balancer)를 설정할 때 사용되는 Ingress 클래스 매개변수를 정의합니다.

targetgroupbindings.elbv2.k8s.aws: 이 CRD는 AWS Load Balancer Controller를 통해 생성된 ALB 또는 NLB의 타겟 그룹과 Kubernetes 서비스 또는 파드를 연결하기 위해 사용됩니다.

 

kubectl get deployment -n kube-system aws-load-balancer-controller

kubectl get deployment -n kube-system aws-load-balancer-controller

aws-load-balancer-controller 배포가 된 것을 확인할 수 있습니다. 


서비스/파드 배포 테스트 with NLB

watch -d kubectl get pod,svc,ep

다음 명령으로 밑의 쉘에서 watch로 모니터링을 하고, 위에서 deployment를 생성합니다.

 

apiVersion: apps/v1
kind: Deployment
metadata: 
  name: deploy-echo
spec: 
  replicas: 2
  selector: 
    matchLabels: 
      app: deploy-websrv
  template: 
    metadata: 
      labels: 
        app: deploy-websrv
    spec: 
      terminationGracePeriodSeconds: 0
      containers: 
      - name: akos-websrv
        image: k8s.gcr.io/echoserver:1.5
        ports: 
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata: 
  name: svc-nlb-ip-type
  annotations: 
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
    service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "8080"
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
spec: 
  ports: 
    - port: 80
      targetPort: 8080
      protocol: TCP
  type: LoadBalancer
  loadBalancerClass: service.k8s.aws/nlb
  selector: 
    app: deploy-websrv

해당 yaml 파일은 Kubernetes 클러스터에 echo 서버 애플리케이션을 배포하고, AWS의 Network Load Balancer(NLB)를 사용하여 인터넷에서 접근할 수 있도록 설정하는 과정을 보여줍니다. 

Deployment 생성

첫 번째 부분은 Deployment 리소스를 정의하고, 이 Deployment echoserver라는 컨테이너 이미지를 기반으로 하는 deploy-echo라는 이름의 애플리케이션을 배포해. 여기서는 2개의 복제본(replicas)을 생성하도록 설정돼 있습니다. 2개의 파드가 실행될 거야.

각 파드는 8080 포트에서 서비스를 제공할 것이고, app: deploy-websrv라는 레이블로 구분됩니다.

Service 생성

두 번째 부분은 Service 리소스를 정의하며, 이 서비스는 생성된 파드들을 NLB를 통해 인터넷에 노출시키는 역할을합니다. annotations에 설정된 여러 옵션들을 보겠습니다.

- service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip는 NLB가 파드의 IP를 직접 타겟으로 사용하도록 설정합니다.

- service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing는 이 로드 밸런서가 인터넷에서 접근 가능하도록 만듭니다.

- service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "8080"는 NLB의 헬스 체크를 위한 포트를 지정합니다.

- service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"는 여러 가용 영역에 걸쳐 로드 밸런싱을 활성화합니다.

type

LoadBalancer loadBalancerClass: service.k8s.aws/nlb 설정을 통해, 이 서비스가 AWS NLB를 사용하도록 구성돼 있고, 서비스는 app: deploy-websrv 레이블을 가진 파드들로 트래픽을 전달할겁니다.

 

ops-view에서 확인할 수 있듯이, replicas=2에 따라서 2개의 파드가 생성된 것을 확인할 수 있습니다. 

그리고 service가 생성된 것을 확인할 수 있습니다.

 

 이 부분은 annotation과 loadBalancerClass와 관련이 있습니다. 

Kubernetes 서비스를 구성할 때 특히 AWS의 Network Load Balancer(NLB)를 사용하는 경우 중요한 역할을 합니다.

Annotations

Kubernetes에서 annotations는 리소스에 메타데이터를 추가하는 데 사용됩니다. 이 메타데이터는 구성 데이터, 라이브러리/도구용 정보 등 리소스를 설명하거나 구성하는 데 사용할 수 있습니다.

특히 AWS의 NLB와 같은 외부 서비스와 통합할 때, annotations를 사용해 서비스의 세부적인 동작 방식을 제어할 수 있습니다.

echo-service-nlb.yaml 파일에서 사용된 몇 가지 주요 annotations를 살펴봅니다

- service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip: 이 어노테이션은 NLB가 트래픽을 파드의 IP 주소로 직접 전달하도록 설정합니다.

기본적으로 NLB는 인스턴스(노드)를 타겟으로 하지만, 이 옵션을 사용하면 더 세밀한 파드 레벨의 로드 밸런싱이 가능해집니다.

- service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing: 이 어노테이션은 생성된 NLB가 인터넷에서 접근 가능한지, 아니면 내부 네트워크에서만 사용되는지를 설정합니다. 여기서는 internet-facing으로 설정되어 인터넷에서 접근 가능한 로드 밸런서가 됩니다.

- service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "8080": NLB에 의해 사용되는 헬스 체크(건강 검사)를 위한 포트를 지정합니다.. 헬스 체크는 서비스가 정상적으로 동작하는지를 확인하기 위해 주기적으로 수행되는 검사입니다.

- service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true": 여러 가용 영역(Availability Zone)에 걸쳐 로드 밸런싱을 활성화합니다. 이로 인해 트래픽이 다양한 지역에 고르게 분산되어 서비스의 가용성과 안정성이 향상됩니다.

 

loadBalancerClass

loadBalancerClass는 서비스가 사용할 로드 밸런서의 클래스를 지정하는 데 사용됩니다.

이 필드를 통해 특정 유형의 로드 밸런서(예: NLB, ALB)를 명시적으로 선택할 수 있어요. loadBalancerClass는 클라우드 공급자가 여러 종류의 로드 밸런서를 지원하는 경우 유용하게 사용됩니다.

loadBalancerClass: service.k8s.aws/nlb

여기서 service.k8s.aws/nlb는 AWS의 Network Load Balancer를 사용하도록 서비스를 구성하라는 의미입니다. 이를 통해 서비스가 NLB를 사용하여 트래픽을 관리하도록 지정할 수 있습니다.

annotations loadBalancerClass를 사용하여, Kubernetes 서비스가 특정 로드 밸런서의 세부 설정을 제어하고, 원하는 로드 밸런서 유형을 명확하게 선택할 수 있게 됩니다. 


확인

kubectl get svc,ep,ingressclassparams,targetgroupbindings

Kubernetes 클러스터에서 서비스(svc), 엔드포인트(ep), 인그레스 클래스 파라미터(ingressclassparams), 그리고 타겟 그룹 바인딩(targetgroupbindings) 리소스의 목록을 조회합니다.

서비스(Services)

- service/kubernetes: 이는 Kubernetes 클러스터의 기본 서비스로, 클러스터 내부에서 Kubernetes API 서버에 접근할 때 사용돼. 클러스터 IP가 10.100.0.1이고, 외부 IP는 없습니다(<none>).

- service/svc-nlb-ip-type: 네가 만든 서비스로, LoadBalancer 타입이고 AWS Network Load Balancer(NLB)를 사용하여 인터넷에 노출돼 있습니다. 클러스터 내부 IP는 10.100.138.108이고, 외부에서 접근 가능한 엔드포인트는 k8s-default-svcnlbip-7c4165d169-004860d36a039944.elb.ap-northeast-2.amazonaws.com로, 이는 AWS에서 생성된 NLB의 DNS 이름입니다.

엔드포인트(Endpoints)

- endpoints/kubernetesendpoints/svc-nlb-ip-type: 각 서비스에 연결된 실제 파드의 IP 주소와 포트를 나타냅니다. svc-nlb-ip-type 서비스는 192.168.1.95:8080 192.168.2.112:8080에서 동작하는 두 개의 파드로 트래픽을 라우팅하고 고 있습니다.

타겟 그룹 바인딩(TargetGroupBindings)

- targetgroupbinding.elbv2.k8s.aws/k8s-default-svcnlbip-50180b1ec3: 이는 svc-nlb-ip-type 서비스에 대한 타겟 그룹 바인딩을 나타내고 있습니다. 이 바인딩은 서비스의 트래픽을 NLB를 통해 파드로 라우팅하는 데 사용됩니다. TARGET-TYPE이 ip로 설정되어 있어서, NLB는 파드의 IP 주소를 직접 타겟으로 삼습니다.

이 정보들을 종합하면, NLB를 통해 svc-nlb-ip-type 서비스를 인터넷에 노출시켰고, 이 서비스는 두 개의 파드로 구성된 deploy-echo 애플리케이션을 포함하고 있음을 알 수 있습니다.

이 구성을 통해, 외부 트래픽은 NLB를 통해 적절한 파드로 라우팅합니다. 

 

타겟그룹

결과를 빨리 보기 위해서 등록 취소 지연(드레이닝 간격) 300초를 60초로 조정해줍니다. 

 

vi echo-service-nlb.yaml

위 명령으로 yaml파일을 수정해줍니다. 

    service.beta.kubernetes.io/aws-load-balancer-target-group-attributes: deregistration_delay.timeout_seconds=60

위 부분을 추가해주고

 

kubectl apply -f echo-service-nlb.yaml

수정된 yaml파일을 apply 해준다. 그리고 service 부분에서 바뀌어서 configured 로그가 생긴것을 확인할 수 있습니다.

 

60초로 바뀐것을 확인할 수 있습니다.

쿠버네티스에서 변화사항을 줬는데, AWS에 반영된것을 확인할 수 있는것입니다다. 로드밸런서 컨트롤러 파드 덕분입니다.. 

 

AWS ELB(NLB) 정보 확인

aws elbv2 describe-load-balancers | jq
aws elbv2 describe-load-balancers --query 'LoadBalancers[*].State.Code' --output text
ALB_ARN=$(aws elbv2 describe-load-balancers --query 'LoadBalancers[?contains(LoadBalancerName, `k8s-default-svcnlbip`) == `true`].LoadBalancerArn' | jq -r '.[0]')
aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN | jq
TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN | jq -r '.TargetGroups[0].TargetGroupArn')
aws elbv2 describe-target-health --target-group-arn $TARGET_GROUP_ARN | jq

AWS Elastic Load Balancing(ELB) 서비스의 Network Load Balancer(NLB)와 연관된 타겟 그룹 및 그 타겟들의 건강 상태를 확인하는 과정입니다.

모든 로드 밸런서의 상태를 조회합니다. 결과 active는 로드 밸런서가 활성 상태이며 정상적으로 작동하고 있음을 나타냅니다. 

 

위에서 저장한 로드 밸런서 ARN에 연결된 타겟 그룹의 정보를 조회합니다. 

 

저장된 타겟 그룹 ARN을 사용해 그 타겟 그룹에 등록된 타겟들의 건강 상태를 조회해. 결과적으로, 각 타겟의 IP 주소, 포트, 가용 영역, 그리고 헬스 상태가 나타나며, 여기서는 두 타겟 모두 healthy 상태임을 확인할 수 있습니다.

 

(콘솔상에서 확인한 타겟그룹으로 보아도 동일합니다.)

kubectl get pod -owide

그리고 다음 명령으로 확인한 파드의 Ip랑 똑같은것을 확인할 수 있습니다. 


로드 밸런서가 노드가 아니라 파드를 직접 목적지로 하여 통신합니다.

AWS NLB와 VPC CNI를 활용한 파드 직접 통신의 이해

AWS의 Network Load Balancer(NLB)는 TargetType ip로 설정될 때, 파드를 직접 목적지로 하여 통신할 수 있습니다. 이 설정은 로드 밸런서의 타겟 그룹에 등록된 타겟의 ID가 파드의 IP 주소로 지정되어 있기 때문에 가능합니다. 이러한 방식은 쿠버네티스 환경에서 각 파드에 대한 세밀한 트래픽 제어와 확장성을 향상시킵니다.

AWS VPC CNI

AWS VPC CNI는 EKS(Elastic Kubernetes Service) 클러스터에서 네이티브 VPC 네트워킹을 가능하게 하는 Kubernetes 네트워크 플러그인입니다. 각 파드는 VPC 내의 IP 주소를 직접 할당받아, VPC 내의 다른 리소스나 인터넷과 직접적으로 통신할 수 있습니다.

파드를 직접 목적지로 사용하는 이유

1. 성능과 효율성 향상: NLB가 파드를 직접 목적지로 사용함으로써, 네트워크 경로를 최적화하고 노드를 거치지 않고 파드로 바로 트래픽을 전송합니다. 이는 성능과 효율성을 높여줍니다.

2. 네트워킹 구성의 간소화: VPC CNI를 사용하면, 각 파드에 VPC의 IP 주소가 직접 할당됩니다. 이는 내부적으로 NAT나 포트 매핑 없이도 통신이 가능하게 하여, Kubernetes 클러스터의 네트워킹 구성을 간소화합니다.

3. 보안 강화: VPC CNI와 함께 사용하면, AWS의 보안 그룹을 파드 수준에서 직접 적용할 수 있습니다. 이는 클러스터의 보안을 강화하는 데 도움을 줍니다.

결론

AWS의 NLB와 VPC CNI를 활용하면, 파드의 IP 주소를 직접 목적지로 사용하여 트래픽을 라우팅하는 것이 가능해집니다. 이는 노드 레벨의 로드 밸런싱보다 많은 이점을 제공하며, AWS EKS 클러스터에서 효율적이고 강력한 네트워킹 구성을 가능하게 합니다.

 

접속 주소 확인

kubectl get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "Pod Web URL = http://"$1 }'

접속주소를 들어가니 잘 나와주는것을 확인할 수 있습니다.

 

kubectl logs -l app=deploy-websrv -f

밑의 터미널에서 로그를 확인하고,

NLB=$(kubectl get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname})
curl -s $NLB

위의 터미널에서는 요청을 보내면, 밑의 터미널에서는 액세스 로그가 확인이 됩니다. 

 

NLB=$(kubectl get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname})
curl -s $NLB
for i in {1..100}; do curl -s $NLB | grep Hostname ; done | sort | uniq -c | sort -nr

그리고 다시 100번의 요청을 보내면 파드가 두개니까, 두 호스트에 대해서 100개 요청이 분산해서 간 것을 볼 수 있습니다. 

 

파드의 갯수를 조절하면서 auto discovery 확인 

while true; do curl -s --connect-timeout 1 $NLB | egrep 'Hostname|client_address'; echo "----------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done

왼쪽 위 터미널에서는 다음 명령으로 지속적으로 curl 요청을 보내도록 하고, 

밑의 터미널에서는 로그를 확인합니다. 

 

kubectl scale deployment deploy-echo --replicas=1

그리고 오른쪽 뒤 터미널에서는 다음 명령으로 레플리카를 1개로 조절합니다. 원래는 두개였습니다. 

타겟그룹 하나가 즉시 드레이닝 되는것을 볼 수 있습니다.

 NLB=$(kubectl get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname})
curl -s $NLB
for i in {1..100}; do curl -s $NLB | grep Hostname ; done | sort | uniq -c | sort -nr

그리고 다시 100번의 요청을 분산해서 보내면 한 호스트에만 100개 요청이 다 간것을 볼 수 있습니다. 

 

 kubectl scale deployment deploy-echo --replicas=3

파드의 갯수를 3개로 다시 조절합니다. 

 

하나 더 뜨고있고, 

 

다시 100개의 요청을 보내면, 따로 작업을 할 필요없이 알아서 파드들에 분산해서 요청을 보내준다.

 

Pod readiness gate : ALB/NLB 대상(ip mode)이 ALB/NLB의 헬스체크에 의해 정상일 경우 해당 파드로 전달할 수 있는 기능

 while true; do aws elbv2 describe-target-health --target-group-arn $TARGET_GROUP_ARN --output text; echo; done

AWS Network Load Balancer(NLB)를 통해 파드의 건강 상태를 지속적으로 모니터링하는 스크립트

 

kubectl label namespace default elbv2.k8s.aws/pod-readiness-gate-inject=enabled
kubectl get ns --show-labels

 

READINESS GATES 항목 추가 확인

 kubectl delete pod --all
 kubectl get pod -owide

그리고 삭제 후 다시 파드 정보를 확인해보면 READINESS GATES 0/1로 생긴것을 알 수 있습니다. 

READINESS GATES 1/1 이 되는것을 확인할 수 있습니다. 

 

Kubernetes의 Pod Readiness Gates와 관련된 중요한 개념을 살펴볼 수 있습니다. 

Pod Readiness Gates는 Kubernetes에서 파드의 준비 상태를 세밀하게 제어할 수 있는 기능을 제공합니다.

kubectl label namespace default elbv2.k8s.aws/pod-readiness-gate-inject=enabled
kubectl get ns --show-labels

kubectl get pod -owide 명령어로 파드를 조회했을 때, 새로 생성된 deploy-echo-7f579ff9d7-2w6cv 파드가 나타났어요. 이 새 파드는 새로운 IP 주소(192.168.2.94)를 가지고 있었고, 처음 조회했을 때는 준비 상태(Readiness)가 아직 완료되지 않았다는 것을 READY 1/1 STATUS Running READINESS GATES 0/1에서 알 수 있습니다.

 

Readiness Gates와의 관련성

- Readiness Gates는 파드가 서비스 트래픽을 받기 전에 충족해야 하는 추가적인 조건을 정의할 수 있게 해주는 기능입니다. 이 경우, AWS Load Balancer Controller는 파드가 로드 밸런서에 의해 트래픽을 받을 준비가 되었는지를 확인하는 데 Readiness Gates를 사용할 수 있습니다.

- 파드가 새로 생성될 때, AWS Load Balancer Controller는 해당 파드가 로드 밸런서의 타겟 그룹에 정상적으로 등록되고 건강 검사를 통과할 때까지 파드의 Readiness 상태를 false로 유지할 수 있습니다.. 이로 인해, 서비스는 오직 건강 검사를 통과한 파드로만 트래픽을 전달하게 되어 애플리케이션의 안정성을 높일 수 있습니다.

위의 흐름은 Kubernetes 클러스터에서 파드의 생명주기 관리와 트래픽 제어를 세밀하게 관리하기 위해 Readiness Gates와 같은 메커니즘을 어떻게 활용할 수 있는지 보여줍니다.

 

kubectl describe pod

다음 명령으로, 원래는 readiness gates라는 항목에 True값이 생깁니다.


Ingress

인그레스 : 클러스터 내부의 서비스(ClusterIP, NodePort, Loadbalancer)를 외부로 노출(HTTP/HTTPS) - Web Proxy 역할

게임 파드와 Service, Ingress 배포

apiVersion: v1
kind: Namespace
metadata: 
  name: game-2048
---
apiVersion: apps/v1
kind: Deployment
metadata: 
  namespace: game-2048
  name: deployment-2048
spec: 
  selector: 
    matchLabels: 
      app.kubernetes.io/name: app-2048
  replicas: 2
  template: 
    metadata: 
      labels: 
        app.kubernetes.io/name: app-2048
    spec: 
      containers: 
      - image: public.ecr.aws/l6m2t8p7/docker-2048:latest
        imagePullPolicy: Always
        name: app-2048
        ports: 
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata: 
  namespace: game-2048
  name: service-2048
spec: 
  ports: 
    - port: 80
      targetPort: 80
      protocol: TCP
  type: NodePort
  selector: 
    app.kubernetes.io/name: app-2048
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata: 
  namespace: game-2048
  name: ingress-2048
  annotations: 
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
spec: 
  ingressClassName: alb
  rules: 
    - http: 
        paths: 
        - path: /
          pathType: Prefix
          backend: 
            service: 
              name: service-2048
              port: 
                number: 80
kubectl apply -f ingress1.yaml

위의 터미널에는 해당 내용의 yaml을 쿠버네티스에 적용합니다.

 

watch -d kubectl get pod,ingress,svc,ep -n game-2048

밑의 터미널에는 다음 명령어로 와치를 걸어둡니다.

 

혹시 yaml내용이 궁금하다면 이런 의미입니다.

Namespace, Deployment, Service, 그리고 Ingress. 각각의 리소스는 다음과 같은 역할을 합니다

Namespace

- game-2048: 이 네임스페이스는 2048 게임과 관련된 모든 Kubernetes 리소스를 격리하여 관리하기 위한 논리적 분리를 제공합니다.

 

Deployment

- deployment-2048: 이 디플로이먼트는 2048 게임 애플리케이션의 파드를 관리합니다. 여기서는 2개의 레플리카(복제본)를 설정하여, 항상 2개의 파드가 실행되도록 보장합니다. 각 파드는 public.ecr.aws/l6m2t8p7/docker-2048:latest 이미지를 사용하여 컨테이너화된 2048 게임 애플리케이션을 실행합니다.

 

Service

- service-2048: 이 서비스는 디플로이먼트에 의해 생성된 파드로 네트워크 트래픽을 라우팅합니다. NodePort 타입으로 설정되어 있어, 클러스터의 모든 노드에서 동일한 포트를 통해 게임에 접근할 수 있도록 합니다. 이 서비스는 80번 포트에서 수신 대기하고, 트래픽을 파드의 80번 포트로 전달합니다.

 

Ingress

- ingress-2048: 이 인그레스는 클러스터 외부에서 서비스로 들어오는 HTTP 트래픽을 관리합니다. alb.ingress.kubernetes.io/scheme: internet-facing 어노테이션은 이 인그레스가 인터넷에서 접근 가능하도록 설정하고, alb.ingress.kubernetes.io/target-type: ip ALB(Application Load Balancer)가 파드의 IP를 직접 타겟으로 설정하도록 합니다. / 경로로 들어오는 모든 트래픽은 service-2048 서비스로 라우팅되어, 사용자가 2048 게임에 접근할 수 있게 됩니다.

kubectl get pod -A

서비스도 생성된것을 확인 가능하다. 

kubectl get ingress -A

인그레스도 생성된것을 확인 가능하다. 

 kubectl get ingress -A 명령어 출력에서 볼 수 있는 것처럼, game-2048 네임스페이스 내에 ingress-2048라는 이름의 인그레스가 성공적으로 생성되었고, AWS의 Application Load Balancer(ALB)를 사용하여 설정된 것을 확인할 수 있습니다. 

이 인그레스는 클러스터 외부에서의 접근을 관리하며, 특정 호스트나 경로에 대한 HTTP 트래픽을 Kubernetes 서비스로 라우팅하는 역할을 합니다.

 

더 자세히 뜯으면 

- CLASS에서 확인할 수 있듯이 alb는 인그레스가 AWS ALB를 사용하여 구성되었음을 나타냅니다. 이는 apiVersion: networking.k8s.io/v1 ingressClassName: alb 설정을 통해 지정됩니다.

- HOSTS: *. 모든 호스트에 대한 요청을 받아들입니다.  특정 호스트를 지정하지 않았기 때문입니다.

- ADDRESS에서 k8s-game2048-ingress2-70d50ce3fd-853601224.ap-northeast-2.elb.amazonaws.com. 이는 생성된 ALB의 DNS 이름으로, 이 주소를 통해 인터넷에서 2048 게임에 접근할 수 있게 됩니다.

- PORTS: 80. 인그레스를 통해 접근할 때 사용되는 포트입니다.

 

해당 어드레스에 나온 ALB의 DNS로 접속을 하니 게임이 잘 접속이 되는걸 확인할 수 있었습니다. 

 

저보다 높으신분? 

 

ALB에 리소스맵이 새로 생겨서 좀 구조를 보기 편해졌습니다. 

파드에 직접 매핑된것도 보이네요 

 

kubectl scale deployment -n game-2048 deployment-2048 --replicas 3

대상그룹을 3개로 늘려보겠습니다. 

 

ALB 리소스맵에서도 3개로 바로 늘어난것 확인할 수 있었습니다. 

External DNS

K8S 서비스/인그레스 생성 시 도메인을 설정하면, AWS(Route 53), Azure(DNS), GCP(Cloud DNS) 에 A 레코드(TXT 레코드)로 자동 생성/삭제

ExternalDNS CTRL 권한 주는 방법 3가지 : Node IAM Role, Static credentials, IRSA

Node IAM Role은 보안적으로 취약함 IRSA을 권장합니다. 

public domain을 가지고 있어야합니다. 

 

저는 위의 도메인을 가지고있습니다.

 

aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." | jq
aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Name"
aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text
MyDnzHostedZoneId=`aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text`
echo $MyDnzHostedZoneId

터미널에서 위 명령으로 route 53 도메인 id를 변수로 지정해줍니다. 

 

while true; do aws route53 list-resource-record-sets --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A']" | jq ; date ; echo ; sleep 1; done

그리고 레코드값을 반복 조회할 수 있습니다.  

MyDnzHostedZoneId=`aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text`
MyDomain=tipy.ee
echo $MyDomain, $MyDnzHostedZoneId

 

$MyDomain과 $MyDnzHostedZoneId에 대해서 환경변수로 설정되어있도록 해야합니다. 

 

ExternalDNS 배포

apiVersion: v1
kind: ServiceAccount
metadata: 
  name: external-dns
  namespace: kube-system
  labels: 
    app.kubernetes.io/name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata: 
  name: external-dns
  labels: 
    app.kubernetes.io/name: external-dns
rules: 
  - apiGroups: [""]
    resources: ["services","endpoints","pods","nodes"]
    verbs: ["get","watch","list"]
  - apiGroups: ["extensions","networking.k8s.io"]
    resources: ["ingresses"]
    verbs: ["get","watch","list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata: 
  name: external-dns-viewer
  labels: 
    app.kubernetes.io/name: external-dns
roleRef: 
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: external-dns
subjects: 
  - kind: ServiceAccount
    name: external-dns
    namespace: kube-system # change to desired namespace: externaldns, kube-addons
---
apiVersion: apps/v1
kind: Deployment
metadata: 
  name: external-dns
  namespace: kube-system
  labels: 
    app.kubernetes.io/name: external-dns
spec: 
  strategy: 
    type: Recreate
  selector: 
    matchLabels: 
      app.kubernetes.io/name: external-dnsm
  template: 
    metadata: 
      labels: 
        app.kubernetes.io/name: external-dns
    spec: 
      serviceAccountName: external-dns
      containers: 
        - name: external-dns
          image: registry.k8s.io/external-dns/external-dns:v0.14.0
          args: 
            - --source=service
            - --source=ingress
            - --domain-filter=${MyDomain} # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
            - --provider=aws
 
            - --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both)
            - --registry=txt
            - --txt-owner-id=${MyDnzHostedZoneId}
          env: 
            - name: AWS_DEFAULT_REGION
              value: ap-northeast-2 # change to region where EKS is installed

위 내용의 yaml파일을 쿠버 환경에 배포해줍니다. 

MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst < externaldns.yaml | kubectl apply -f -

도메인 이름과 호스티드 존에 대해서 변수를 치환해서 배포를 합니다. 

 

어플리케이션 Deployment 배포 

cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tetris
  labels:
    app: tetris
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tetris
  template:
    metadata:
      labels:
        app: tetris
    spec:
      containers:
      - name: tetris
        image: bsord/tetris
---
apiVersion: v1
kind: Service
metadata:
  name: tetris
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "http"
    #service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "80"
spec:
  selector:
    app: tetris
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  type: LoadBalancer
  loadBalancerClass: service.k8s.aws/nlb
EOF

배포 해줍니다. 

 

배포된 것 확인할 수 있습니다. 

kubectl annotate service tetris "external-dns.alpha.kubernetes.io/hostname=tetris.$MyDomain"

도메인을 셋팅하는 annotation을 설정해줍니다.

kubectl logs deploy/external-dns -n kube-system -f

external-dens의 로그를 감시하는 터미널을 확인해보면 

알아서 레코드를 설정해주는것을 볼 수 있다.

 

route53에서 확인해보면, 알아서 tetris.tipy.ee로 A와 TXT레코드를 생성한것을 확인 가능합니다. 

 

해당 A레코드를 자세하게 확인해보면, Alias가 NLB로 되어있는것을 볼 수 있습니다.

 


Network Policies with VPC CNI

ssh ec2-user@$N1 sudo /opt/cni/bin/aws-eks-na-cli ebpf progs

실행중인 eBPF를 확인하려고 하는데, 

 

ssh ec2-user@$N1 mount | grep -i bpf
ssh ec2-user@$N1 df -a | grep -i bpf

이미 마운트가 되어있음

 

샘플 어플리케이션 배포 및 네트워크 정책 적용 실습 

 

kubectl get pod,svc
kubectl get pod,svc -n another-ns

다음 명령으로 해당 아키텍쳐의 모양으로 배포가 되었는지 확인한다 .

 

통신 확인 

kubectl exec -it another-client-one -n another-ns -- curl demo-app
kubectl exec -it another-client-one -n another-ns -- curl demo-app.default
kubectl exec -it another-client-two -n another-ns -- curl demo-app.default.svc

1. 첫 번째 시도에서의 실패:

- kubectl exec -it another-client-one -n another-ns -- curl demo-app 명령어를 실행했을 때, curl: (6) Could not resolve host: demo-app 오류가 발생

- 이 오류는 demo-app 호스트 이름을 해석할 수 없음을 의미합니다. 여기서 중요한 포인트는, 커맨드가 another-ns 네임스페이스에서 실행되었고, demo-app에 대한 네임스페이스를 명시적으로 지정하지 않았기 때문에 발생한 문제

2. 두 번째 시도에서의 성공:

- kubectl exec -it another-client-one -n another-ns -- curl demo-app.default 명령어를 통해, 이번에는 demo-app 애플리케이션에 성공적으로 접근할 수 있었습니다. 

- 이 경우에 demo-app.default demo-app 서비스가 위치한 default 네임스페이스를 명확하게 지정해줬습니다. 결과적으로, 해당 HTML 페이지(즉, Amazon EKS 환영 메시지)에 접근할 수 있었습니다.

3. 세 번째 시도에서도 성공:

- kubectl exec -it another-client-two -n another-ns -- curl demo-app.default.svc 명령어는 세 번째 시도에서도 성공했어요. 

- 여기서는 demo-app.default.svc를 사용해 서비스의 전체 DNS 이름을 명확하게 지정했습니다. .svc는 Kubernetes 서비스의 기본 도메인을 나타내며, 이 방식 역시 성공적으로 demo-app에 접근할 수 있도록 해줬습니다.

while true; do kubectl exec -it client-one -- curl --connect-timeout 1 demo-app ; date; sleep 1; done

다음 명령으로 통신 시도를 계속 하도록 합니다.

 

트래픽을 거부하는 정책을 적용해봅니다. 

# 정책 적용
cat advanced/policies/01-deny-all-ingress.yaml | yh
kubectl apply -f advanced/policies/01-deny-all-ingress.yaml
kubectl get networkpolicy

1단계: 네트워크 정책 파일 확인

- 01-deny-all-ingress.yaml 파일에는 demo-app이라는 레이블을 가진 파드로의 모든 들어오는(Ingress) 트래픽을 차단하는 네트워크 정책이 정의되어 있습니다. 이 정책은 해당 앱으로 들어오는 모든 연결을 기본적으로 차단하도록 설정합니다.

2단계: 네트워크 정책 적용

- kubectl apply -f advanced/policies/01-deny-all-ingress.yaml 명령을 사용하여 정책을 적용했고, demo-app-deny-all이라는 이름으로 네트워크 정책이 생성되었습니다.

3단계: 네트워크 정책 확인

- kubectl get networkpolicy 명령을 통해 새로 생성된 네트워크 정책의 존재를 확인할 수 있습니다. 이 정책은 demo-app이라는 레이블을 가진 파드를 대상으로 하며, 이 파드로의 모든 들어오는 트래픽을 차단합니다.

 

실행 결과

- 초기에 another-client-one another-client-two 파드에서 demo-app 서비스에 대한 curl 요청을 성공적으로 수행할 수 있었습니다.  이는 네트워크 정책이 아직 적용되기 전이었기 때문입니다.

- demo-app-deny-all 네트워크 정책을 적용한 후에는, 이 정책이 demo-app으로의 모든 들어오는 트래픽을 차단하기 때문에, 같은 curl 요청을 수행할 경우 연결이 차단될 것입니다(밑의 터미널에서 차단된 모습을 확인할 수 있습니다.)

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata: 
  name: demo-app-deny-all
spec: 
  podSelector: 
    matchLabels: 
      app: demo-app
  policyTypes: 
  - Ingress

- podSelector: 이 섹션은 정책이 적용될 파드를 선택하는 데 사용됩니다. matchLabels를 통해 app: demo-app 레이블을 가진 파드를 대상으로 지정하고 있어요. 즉, 이 네트워크 정책은 app 레이블의 값이 demo-app인 모든 파드에 적용됩니다.

- policyTypes: 여기서는 정책의 유형을 지정합니다. - Ingress라고 명시되어 있는데, 이는 들어오는 트래픽에 대한 정책임을 나타냅니다. 즉, 이 네트워크 정책은 들어오는(Ingress) 트래픽에만 적용되며, 클러스터 외부 또는 클러스터 내의 다른 파드로부터 해당 파드로 들어오는 모든 트래픽을 차단합니다.

 

트래픽 차단 방식

이 네트워크 정책은 명시적으로 허용 규칙을 포함하고 있지 않습니다. Kubernetes에서 네트워크 정책은 기본적으로 "명시적 허용"을 기반으로 동작합니다. 즉, 특정 트래픽이 정책에 의해 명시적으로 허용되지 않는 한, 그 트래픽은 차단됩니다.

demo-app-deny-all 정책은 Ingress 트래픽을 대상으로 하며, 어떠한 트래픽도 명시적으로 허용하지 않기 때문에, app: demo-app 레이블을 가진 파드로 들어오는 모든 트래픽을 차단하게 됩니다. 결과적으로, 이 정책이 적용된 파드는 클러스터 내외부에서 발생하는 모든 들어오는 연결을 받지 않게 됩니다.

 

결론 

ingress 밑에 명시적으로 허용한것이 없기때문에  파드로의 들어오는 트래픽이 모두 차단한다. 

 

ingress 밑에 명시적으로 허용한것이 없기때문에  파드로의 들어오는 트래픽이 모두 차단한다. 

kubectl delete -f advanced/policies/01-deny-all-ingress.yaml

위 명령으로 정책을 삭제하니 바로 접근이 가능해집니다. 

 

클라이언트1 만 허용 

cat advanced/policies/03-allow-ingress-from-samens-client-one.yaml | yh
kubectl apply -f advanced/policies/03-allow-ingress-from-samens-client-one.yaml
kubectl get networkpolicy

demo-app 레이블을 가진 파드로 들어오는 트래픽 중에서 특정 조건을 만족하는 트래픽만을 허용하는 네트워크 정책을 정의하고 있습니다.

이 정책은 app: demo-app 레이블을 가진 파드를 대상으로 하며, 특정 소스(app: client-one 레이블을 가진 파드)로부터의 들어오는 트래픽을 허용하는 규칙을 포함하고 있습니다.

 

정책 구성

- podSelector: app: demo-app 레이블을 가진 파드를 대상으로 하는 네트워크 정책을 설정합니다.

- ingress: 이 섹션은 들어오는 트래픽에 대한 규칙을 정의합니다.

- from: 이하 규칙은 허용할 트래픽의 출처를 정의합니다.

- podSelector: app: client-one 레이블을 가진 파드로부터 들어오는 트래픽을 허용합니다.

 

네트워크 정책 적용 결과

이 정책을 적용함으로써 app: demo-app 레이블을 가진 파드는 app: client-one 레이블을 가진 파드로부터의 들어오는 트래픽만을 수신할 수 있게 됩니다.

이전에 적용된 demo-app-deny-all 정책은 모든 들어오는 트래픽을 차단했지만, 이 새로운 정책은 특정 출처로부터의 트래픽은 허용하는 예외 규칙을 제공합니다. 결과적으로, client-one 애플리케이션이 실행 중인 파드만이 demo-app 애플리케이션에 접근할 수 있게 됩니다.

cat advanced/policies/04-allow-ingress-from-xns.yaml | yh
kubectl apply -f advanced/policies/04-allow-ingress-from-xns.yaml
kubectl get networkpolicy

namespace another-ns에 있는 것들은 다 허용하도록 정책 적용한다. 

 

kubectl exec -it another-client-two -n another-ns -- curl --connect-timeout 1 demo-app.default

another-ns 에서 접근을 하려니 역시 잘 접근됩니다. 

 

송신 트래픽 차단 

while true; do kubectl exec -it client-one -- curl --connect-timeout 1 google.com ; date; sleep 1; done

밑의 터미널에서는 계속 접근을 시도한다. 

 

cat advanced/policies/06-deny-egress-from-client-one.yaml | yh
kubectl apply -f advanced/policies/06-deny-egress-from-client-one.yaml
kubectl get networkpolicy

위 터미널에서는 다음 명령으로 새로운 송신 트래픽 차단 정책을 적용한다. 

정책을 적용하니, 원래는 구글에 잘 접근이 되다가, 타임아웃이 나는것을 확인 가능하다. 

 

06-deny-egress-from-client-one.yaml 파일은 특정 파드로부터의 나가는(egress) 트래픽을 차단하는 네트워크 정책을 정의하고 있습니다. 이 정책은 app: client-one 레이블을 가진 파드를 대상으로 하며, 이 파드로부터 나가는 모든 트래픽을 기본적으로 차단합니다.

 

정책 구성

- podSelector: app: client-one 레이블을 가진 파드를 대상으로 하는 네트워크 정책을 설정합니다.

- egress: 나가는 트래픽에 대한 규칙을 정의합니다. 여기서는 egress: []로 설정되어 있어, 즉, 어떠한 나가는 트래픽도 허용하지 않겠다는 의미입니다.

- policyTypes: 네트워크 정책의 유형을 지정합니다. 여기서는 - Egress라고 명시되어 있어, 이 정책이 나가는 트래픽에만 적용됨을 나타냅니다.

 

네트워크 정책 적용의 효과

이 정책을 적용하면, app: client-one 레이블을 가진 파드로부터의 모든 나가는 트래픽이 차단됩니다. 이는 해당 파드가 클러스터 내 또는 클러스터 외부의 어떠한 서비스나 리소스에도 접근하지 못하게 만듭니다.

예를 들어, 이 파드가 다른 서비스에 HTTP 요청을 보내거나, 외부 인터넷에 데이터를 전송하는 등의 행위가 모두 차단됩니다.

kubectl exec -it client-one -- nslookup demo-app

도메인 질의도 안됩니다.


송신 트래픽 허용 : DNS 트래픽을 포함하여 여러 포트 및 네임스페이스에서의 송신을 허용

cat advanced/policies/08-allow-egress-to-demo-app.yaml | yh
kubectl apply -f advanced/policies/08-allow-egress-to-demo-app.yaml
kubectl get networkpolicy

08-allow-egress-to-demo-app.yaml 파일은 app: client-one 레이블을 가진 파드에 대해 특정 조건 하에 나가는(egress) 트래픽을 허용하는 네트워크 정책을 정의합니다. 특정 DNS 조회와 demo-app으로의 HTTP 요청을 허용합니다.

다시 구글과 통신 되는것을 확인할 수 있습니다. 

정책 구성

- podSelector: app: client-one 레이블을 가진 파드를 대상으로 합니다.

- egress:

- 첫 번째 규칙은 kube-system 네임스페이스의 kube-dns 서비스(DNS 서비스)에 대한 UDP 53 포트로의 트래픽을 허용합니다. 이는 파드가 도메인 이름을 IP 주소로 변환할 수 있게 해주어, 외부 서비스에 대한 접근을 가능하게 합니다.

- 두 번째 규칙은 app: demo-app 레이블을 가진 파드로의 TCP 80 포트로의 트래픽을 허용합니다. 이는 client-one 애플리케이션이 demo-app 애플리케이션과 통신할 수 있게 해줍니다.

 

네트워크 정책 적용의 효과

이 정책을 적용함으로써, client-one 애플리케이션이 실행 중인 파드는 다음 두 가지 특정 조건 하에 외부와 통신할 수 있습니다:

1. DNS 조회를 수행할 수 있습니다(kube-dns에 대한 UDP 트래픽).

2. demo-app 애플리케이션과 HTTP를 통해 통신할 수 있습니다(TCP 80 포트).

이전에 적용된 client-one-deny-egress 정책은 client-one 앱으로부터 모든 나가는 트래픽을 차단했지만, 이 새로운 정책은 특정 목적지로의 트래픽은 허용하는 예외 규칙을 추가합니다. 따라서, client-one 애플리케이션이 demo-app과의 필수 통신과 DNS 조회를 수행할 수 있도록 네트워크 접근성을 세밀하게 제어합니다.

 

JUNE .

20'S LIFE IN SYDNEY and BUSAN

    이미지 맵

    DevOps Study/Kubernetes 다른 글

    이전 글

    다음 글