PKOS2) Kubernetes 네트워크 & 스토리지

CloudNet@팀에서 진행하는 쿠버네티스 실무 실습 스터디 참가글입니다.

24단계 실습으로 정복하는 쿠버네티스 도서 내용을 기반으로 진행됩니다.


1. k8s network

1.1. AWS VPC CNI 소개

CNI란?

CNI는 Container Network Interface의 줄임말이다. (참고: k8s 네트워킹 공식문서 링크)

Kubernetes는 컨테이너간 통신에 있어, 네트워크 충돌을 방지하기 위해 CNI 플러그인을 사용하여 네트워크 및 보안 기능을 관리한다.

k8s는 컨테이너화된 어플리케이션과 서비스를 관리하기 위한 플랫폼이다. 따라서 쿠버네티스 플랫폼에 배포된 앱들은 그보다 소수인 호스트 장비와 네트워크를 공유한다. 일반적으로 다수의 앱이 하나의 장비에서 운영된다면, 포트번호에 따른 앱 구분등으로 해결할 수 있다. 하지만 대규모의, 탄력적인 클러스터 환경에서 포트번호에 따른 구분으로는 요구사항을 해결하기가 쉽지 않다. 이에대해 k8s는 CNI 플러그인을 사용하여 문제를 해결한다.

CNI의 작동을 이해하기 위해서는 다음의 4가지 네트워킹 문제를 쿠버와 CNI가 어떻게 해결했는지 살펴보아야 할것이다.

  1. 노드와 파드의 통신
  2. 노드의 파드 간 통신
  3. 파드와 서비스 간 통신
  4. 외부와 서비스 간 통신

AWS VPC CNI란?

AWS VPC CNI는 AWS ENI(Elastic Network Interfaces)를 사용하는 쿠버네티스 네트워크 플러그인이다. (참고: VPC-CNI Github, EKS BP, Proposal)

앞서말한 4가지 숙제고려사항에 대해서, AWS VPC CNI는 호스트 장비에(EC2) 다수의 NIC(ENI)를 생성하고, 각 ENI와 파드를 매칭하는 방식으로 문제를 해결 하였다.

쿠버네티스 네트워크 모델에 대해서, 위의 4가지 문제 사항은 다음과 같은 근본적인 요구사항을 만족하여야 한다.

  • 파드는 NAT 없이 노드 상의 모든 파드와 통신할 수 있다.
  • 노드 상의 에이전트(예: 시스템 데몬, kubelet)는 해당 노드의 모든 파드와 통신할 수 있다.

이 근본 요구사항을 충족시키면서, 앞서 말한 ‘리소스간의 통신 문제를 어떻게 해결하는가?’가 k8s 네트워크 플러그인들의 차이점이다.

AWS VPC CNI는 노드(EC2)의 주 ENI외에도, 사전 준비된(warm pool) ENI와 보조 IP를 필요에 따라 할당한다(보조 IP모드). 이 방식을 통해 기존의 VPC와 리소스들에 대해서 동일한?유사한 수준의 네트워킹을 제공할 수 있다. 해당 모드는 필연적으로 각 노드의 유형에 따라 배포할 수 있는 최대 파드의 수의 제한이 생긴다. 유형마다 사용가능한 최대 ENI수가 제한되어있기 때문이다. 이러한 파드 최대 수의 제약에 대해서, ENI에 prefix를 위임함으로써 한 노드에 더 많은 파드를 배치할 수 있다(접두사 위임모드..?prefix delegation)

AWS VPC CNI와 Calico CNI 차이

조금 더 간단히 요약하면, AWS VPC CNI는 (기본적으로)개별파드에 ENI를 부여하여 파드/노드 간 직접 통신을 가능하게 하였다로 이해하였다.

1.2. 노드의 기본 네트워크 정보 확인

  • Network 네임스페이스는 호스트(Root)와 파드 별(Per Pod)로 구분된다
  • 특정한 파드(kube-proxy, aws-node)는 호스트(Root)의 IP를 그대로 사용한다
  • t3.medium 의 경우 ENI 마다 최대 6개의 IP를 가질 수 있다
  • ENI0, ENI1 으로 2개의 ENI는 자신의 IP 이외에 추가적으로 5개의 보조 프라이빗 IP를 가질수 있다
  • coredns 파드는 veth 으로 호스트에는 eniY@ifN 인터페이스와 파드에 eth0 과 연결되어 있다

# [실습] 보조 IPv4 주소를 파드가 사용하는지 확인

# ebs-csi-node 파드 IP 정보 확인
kubectl get pod -n kube-system -l app=ebs-csi-node -owide

# NAME                 READY   STATUS    RESTARTS   AGE    IP              NODE                  NOMINATED NODE   READINESS GATES
# ebs-csi-node-4vp6k   3/3     Running   0          167m   172.30.91.32    i-0992018185e1cba18   <none>           <none>
# ebs-csi-node-67fwl   3/3     Running   0          168m   172.30.63.160   i-0cd15751dc6645258   <none>           <none>
# ebs-csi-node-n4fcm   3/3     Running   0          167m   172.30.49.176   i-0bea29559ca4a58d0   <none>           <none>


# 노드의 라우팅 정보 확인 >> EC2 네트워크 정보의 '보조 프라이빗 IPv4 주소'와 비교해보자
ssh -i ~/.ssh/id_rsa ubuntu@api.$KOPS_CLUSTER_NAME ip -c route
# default via 172.30.32.1 dev ens5 proto dhcp src 172.30.58.175 metric 100
# 172.30.32.0/19 dev ens5 proto kernel scope link src 172.30.58.175
# 172.30.32.1 dev ens5 proto dhcp scope link src 172.30.58.175 metric 100
# 172.30.61.96 dev enic70b4588754 scope link
# 172.30.63.160 dev eni59c4f0281e7 scope link
# 172.30.63.161 dev eni6c6b1d55a6d scope link
# 172.30.63.162 dev eni4c1325040d9 scope link

ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP ip -c route
# default via 172.30.32.1 dev ens5 proto dhcp src 172.30.41.131 metric 100
# 172.30.32.0/19 dev ens5 proto kernel scope link src 172.30.41.131
# 172.30.32.1 dev ens5 proto dhcp scope link src 172.30.41.131 metric 100
# 172.30.49.176 dev eni65bc4ab1c8f scope link
# 172.30.49.177 dev eni23ef86f11da scope link
# 172.30.49.178 dev enid4f7e488add scope link

ssh -i ~/.ssh/id_rsa ubuntu@$W2PIP ip -c route
# default via 172.30.64.1 dev ens5 proto dhcp src 172.30.70.149 metric 100
# 172.30.64.0/19 dev ens5 proto kernel scope link src 172.30.70.149
# 172.30.64.1 dev ens5 proto dhcp scope link src 172.30.70.149 metric 100
# 172.30.91.32 dev eni36bf37dfc39 scope link
# 172.30.91.33 dev eni5a7011df882 scope link
# 172.30.91.34 dev eniefa7bfdb9ca scope link
# 172.30.91.35 dev enib8de5f6c371 scope link

# [실습] 테스트용 파드 생성

# [터미널1~2] 워커 노드 1~2 모니터링
ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP
watch -d "ip link | egrep 'ens5|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"

ssh -i ~/.ssh/id_rsa ubuntu@$W2PIP
watch -d "ip link | egrep 'ens5|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"

# 테스트용 파드 netshoot-pod 생성
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: netshoot-pod
spec:
  replicas: 2
  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})

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

파드가 생성되면, 워커 노드에 eniY@ifN 추가되고 라우팅 테이블에도 정보가 추가된다

파드가 생성 시, 워커노드 라우팅 테이블 모니터링

1.3. 노드의 파드간 통신

AWS VPC CNI 경우 각 파드에 ENI와 IP가 부여되어 있기 때문에, 별도의 오버레이(Overlay) 통신 기술 없이 VPC 친화적으로 파드간 직접 통신이 가능하다

파드간 통신 시 과정 (링크)

https://github.com/aws/amazon-vpc-cni-k8s/blob/master/docs/cni-proposal.md
# [실습] 파드간 통신 테스트 및 확인

# 파드 IP 변수 지정
PODIP1=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[0].status.podIP})
PODIP2=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[1].status.podIP})

# 파드1 Shell 에서 파드2로 ping 테스트
kubectl exec -it $PODNAME1 -- ping -c 2 $PODIP2

# 파드2 Shell 에서 파드1로 ping 테스트
kubectl exec -it $PODNAME2 -- ping -c 2 $PODIP1

# 워커 노드 EC2 : TCPDUMP 확인
sudo tcpdump -i any -nn icmp
sudo tcpdump -i ens5 -nn icmp

[워커 노드1]
# routing policy database management 확인
ip rule

# 0:	from all lookup local
# 512:	from all to 172.30.49.176 lookup main
# 512:	from all to 172.30.49.177 lookup main
# 512:	from all to 172.30.49.178 lookup main
# 512:	from all to 172.30.49.179 lookup main
# 1024:	from all fwmark 0x80/0x80 lookup main
# 32766:	from all lookup main
# 32767:	from all lookup default

# routing table management 확인
ip route show table local

# local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
# local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
# broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
# local 169.254.20.10 dev nodelocaldns proto kernel scope host src 169.254.20.10
# local 172.30.41.131 dev ens5 proto kernel scope host src 172.30.41.131
# broadcast 172.30.63.255 dev ens5 proto kernel scope link src 172.30.41.131

# 디폴트 네트워크 정보를 ens5 을 통해서 빠져나간다
ip route show table main

#default via 172.30.32.1 dev ens5 proto dhcp src 172.30.41.131 metric 100

sudo tcpdump -i any -nn icmp 명령어로 패킷 확인
sudo tcpdump -i ens5 -nn icmp 명령어로 패킷 확인

1.4. 파드의 외부간 통신

파드에서 외부 통신 흐름> iptable의 SNAT 을 통하여 노드의 eth0 IP로 변경되어서 외부와 통신됨

https://github.com/aws/amazon-vpc-cni-k8s/blob/master/docs/cni-proposal.md

# [실습] 파드에서 외부 통신 테스트 및 확인

# 파드 shell 실행 후 외부로 ping 테스트 & 워커 노드에서 tcpdump 및 iptables 정보 확인

# 작업용 EC2 : pod-1 Shell 에서 외부로 ping
kubectl exec -it $PODNAME1 -- ping -c 1 www.google.com
kubectl exec -it $PODNAME1 -- ping -i 0.1 www.google.com

# 워커 노드 EC2 : 퍼블릭IP 확인, TCPDUMP 확인
curl -s ipinfo.io/ip ; echo
sudo tcpdump -i any -nn icmp
sudo tcpdump -i ens5 -nn icmp

# 작업용 EC2 : pod-1 Shell 에서 외부 접속 확인 - 공인IP는 어떤 주소인가?
## The right way to check the weather - 링크
kubectl exec -it $PODNAME1 -- curl -s ipinfo.io/ip ; echo
kubectl exec -it $PODNAME1 -- curl -s wttr.in/seoul
kubectl exec -it $PODNAME1 -- curl -s wttr.in/seoul?format=3
kubectl exec -it $PODNAME1 -- curl -s wttr.in/Moon
kubectl exec -it $PODNAME1 -- curl -s wttr.in/:help

# 워커 노드 EC2
## 출력된 결과를 보고 어떻게 빠져나가는지 고민해보자! > 아직 잘 모르겠습니다ㅠㅠ
ip rule
ip route show table main
sudo iptables -L -n -v -t nat
sudo iptables -t nat -S


# 파드가 외부와 통신시에는 아래 처럼 'AWS-SNAT-CHAIN-0, AWS-SNAT-CHAIN-1' 룰(rule)에 의해서 SNAT 되어서 외부와 통신!
# 참고로 뒤 IP는 eth0(ENI 첫번째)의 IP 주소이다
# --random-fully 동작 - 링크1  링크2
sudo iptables -t nat -S | grep 'A AWS-SNAT-CHAIN'
-A AWS-SNAT-CHAIN-0 ! -d 172.30.0.0/16 -m comment --comment "AWS SNAT CHAIN" -j AWS-SNAT-CHAIN-1
-A AWS-SNAT-CHAIN-1 ! -o vlan+ -m comment --comment "AWS, SNAT" -m addrtype ! --dst-type LOCAL -j SNAT --to-source 172.30.41.131 --random-fully
외부 통신 tcpdump

워커노드1의 PublicIP 3.36.115.182, Private IP 172.30.41.131

외부망(구글,142.250.207.4) 통신시 iptables의 규칙에 따라서 172.30.41.131로 SNAT 되서 나가는것인지.. 후반부 실습쪽은 아직 이해가 부족합니다. 복습영상보고 다시한번 해봐야할것같습니다.

1.5. 노드의 최대 파드 수 제한

Secondary IPv4 addresses : 인스턴스 유형에 최대 ENI 갯수와 할당 가능 IP 수를 조합하여 선정

워커 노드의 인스턴스 타입 별 파드 생성 갯수 제한

  • 인스턴스 타입 별 ENI 최대 갯수와 할당 가능한 최대 IP 갯수에 따라서 파드 배치 갯수가 결정됨
  • 단, aws-node 와 kube-proxy 파드는 호스트의 IP를 사용함으로 최대 갯수에서 제외함
최대 파드 생성 갯수 : 
(Number of network interfaces for the instance type × (the number of IP addressess per network interface - 1)) + 2

# t3 타입의 정보(필터) 확인
aws ec2 describe-instance-types --filters Name=instance-type,Values=t3.* \
 --query "InstanceTypes[].{Type: InstanceType, MaxENI: NetworkInfo.MaximumNetworkInterfaces, IPv4addr: NetworkInfo.Ipv4AddressesPerInterface}" \
 --output table

# --------------------------------------
# |        DescribeInstanceTypes       |
# +----------+----------+--------------+
# | IPv4addr | MaxENI   |    Type      |
# +----------+----------+--------------+
# |  15      |  4       |  t3.2xlarge  |
# |  15      |  4       |  t3.xlarge   |
# |  6       |  3       |  t3.medium   |
# |  12      |  3       |  t3.large    |
# |  2       |  2       |  t3.nano     |
# |  4       |  3       |  t3.small    |
# |  2       |  2       |  t3.micro    |
# +----------+----------+--------------+

# 파드 사용 가능 계산 예시 : 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개

# 워커노드 상세 정보 확인 : 노드 상세 정보의 Allocatable 에 pods 에 17개 정보 확인
kubectl describe node | grep Allocatable: -A6

1.6. 최대 파드 수 설정

해결 방안 : Prefix Delegation, WARM & MIN IP/Prefix Targets, Custom Network

# [실습] 워커 노드에 50개 이상 파드 배포

# 파드 100개 배포
kubectl apply -f ~/pkos/2/nginx-dp.yaml
kubectl scale deployment nginx-deployment --replicas=100

# 노드 상세 정보 확인 : 노드 상세 정보의 Allocatable 에 노드별 최대 생성 가능한 pods 정보 확인 - 각각 마스터 노드, 워커 노드
kubectl describe node | grep Allocatable: -A6

# Allocatable:
#   cpu:                2
#   ephemeral-storage:  59763732382
#   hugepages-1Gi:      0
#   hugepages-2Mi:      0
#   memory:             3854320Ki
#   pods:               100

# node의 daemonset(ds) 정보 확인
# ENABLE_PREFIX_DELEGATION / WARM_ENI_TARGET 수정으로 가능
kubectl describe ds -n kube-system aws-node | grep ADDITIONAL_ENI_TAGS: -A22
      # ADDITIONAL_ENI_TAGS:                    {"KubernetesCluster":"devxmonitor.click","kubernetes.io/cluster/devxmonitor.click":"owned"}
      # AWS_VPC_CNI_NODE_PORT_SUPPORT:          true
      # AWS_VPC_ENI_MTU:                        9001
      # AWS_VPC_K8S_CNI_CONFIGURE_RPFILTER:     false
      # AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG:     false
      # AWS_VPC_K8S_CNI_EXTERNALSNAT:           false
      # AWS_VPC_K8S_CNI_LOGLEVEL:               DEBUG
      # AWS_VPC_K8S_CNI_LOG_FILE:               /host/var/log/aws-routed-eni/ipamd.log
      # AWS_VPC_K8S_CNI_RANDOMIZESNAT:          prng
      # AWS_VPC_K8S_CNI_VETHPREFIX:             eni
      # AWS_VPC_K8S_PLUGIN_LOG_FILE:            /var/log/aws-routed-eni/plugin.log
      # AWS_VPC_K8S_PLUGIN_LOG_LEVEL:           DEBUG
      # DISABLE_INTROSPECTION:                  false
      # DISABLE_METRICS:                        false
      # DISABLE_NETWORK_RESOURCE_PROVISIONING:  false
      # ENABLE_IPv4:                            true
      # ENABLE_IPv6:                            false
      # ENABLE_POD_ENI:                         false
      # ENABLE_PREFIX_DELEGATION:               true
      # WARM_ENI_TARGET:                        1
      # WARM_PREFIX_TARGET:                     1
      # MY_NODE_NAME:                            (v1:spec.nodeName)
      # CLUSTER_NAME:                           devxmonitor.click

1.7. Service & AWS LoadBalancer CNTLR

서비스는 파드 집합에서 실행중인 애플리케이션을 네트워크 서비스로 노출하는 추상화 방법이다. (k8s 문서)

쿠버네티스 환경에서 파드는 언제라도 삭제되고 재생성될 수 있다. 그때마다 파드의 IP값들이 달라지기 때문에, 안정적으로 진입점 관리/ 노출하기 위한 리소스가 서비스이다.

ClusterIP 타입

서비스를 클러스터-내부 IP에 노출시킨다. 이 값을 선택하면 클러스터 내에서만 서비스에 도달할 수 있다. 서비스 생성시 기본적으로 생기는 기본값이다.

NodePort 타입

고정 포트 (NodePort)로 각 노드의 IP에 서비스를 노출시킨다. NodePort 서비스가 라우팅되는 ClusterIP 서비스가 자동으로 생성된다. <NodeIP>:<NodePort>를 요청하여, 클러스터 외부에서 NodePort 서비스에 접속할 수 있다.

LoadBalancer 타입 (기본 모드) : NLB 인스턴스 유형

클라우드 공급자의 로드 밸런서를 사용하여 서비스를 외부에 노출시킨다. 외부 로드 밸런서가 라우팅되는 NodePort와 ClusterIP 서비스가 자동으로 생성된다.

# [실습] 서비스/파드 배포 테스트 with NLB

# 작업용 EC2 - 디플로이먼트 & 서비스 생성
cat ~/pkos/2/echo-service-nlb.yaml | yh
kubectl apply -f ~/pkos/2/echo-service-nlb.yaml
# 확인
kubectl get deploy,pod
kubectl get crd
kubectl get svc,ep,ingressclassparams,targetgroupbindings
kubectl get targetgroupbindings -o json | jq

# AWS ELB(NLB) 정보 확인
aws elbv2 describe-load-balancers | jq
aws elbv2 describe-load-balancers --query 'LoadBalancers[*].State.Code' --output text

# 웹 접속 주소 확인
kubectl get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "Pod Web URL = http://"$1 }'
# Pod Web URL = http://k8s-default-svcnlbip-238b2e929d-d36bc5ff7add713e.elb.ap-northeast-2.amazonaws.com


# 파드 로깅 모니터링
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
for i in {1..100}; do curl -s $NLB | grep Hostname ; done | sort | uniq -c | sort -nr
NLB 접속 테스트

1.8. Ingress

인그레스는 클러스터 외부에서 클러스터 내부 서비스로 HTTP와 HTTPS 경로를 노출한다. 트래픽 라우팅은 인그레스 리소스에 정의된 규칙에 의해 컨트롤된다. (링크)

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

AWS Load Balancer Controller + Ingress (ALB) IP 모드 동작 with AWS VPC CNI

[도전]Ingress(with 도메인, 단일 ALB 사용)에 PATH /mario 는 mario 게임 접속하게 설정하고, /tetris 는 tetris 게임에 접속하게 설정하고, SSL(https strip) 적용 해보세요

  • 결과적으로는 실패하였음
    • AWS LB, External DNS, SSL 적용은 완료
    • 실제 PATH별 경로 접근시 502 Bad Gateway 발생
  • 게임은 잘 접속되나싶어서 개별접속 시도해보았으나 실패
    • type: ClusterIP > type: LoadBalancer 변경
    • 응답이 없다는 ERR_EMPTY_RESPONSE 발생
  • 낮에 재시도 예정
왜 502가 뜨지..
type: LoadBalancer(CLB)로 접속 시도시
단일 Ingress ALB 생성
ALB path기반 라우팅

# ingress와 연결될 리소스 배포 (ns,deploy,svc)

---
# namespace 생성
apiVersion: v1
kind: Namespace
metadata:
  name: game
---
# mario deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mario
  labels:
    app: mario
  namespace: game
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mario
  template:
    metadata:
      labels:
        app: mario
    spec:
      containers:
      - name: mario
        image: pengbai/docker-supermario
---
# mario sevice
apiVersion: v1
kind: Service
metadata:
  name: mario
  namespace: game
spec:
  selector:
    app: mario
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8000
  type: ClusterIP
---
# tetris deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tetris
  labels:
    app: tetris
  namespace: game
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tetris
  template:
    metadata:
      labels:
        app: tetris
    spec:
      containers:
      - name: tetris
        image: bsord/tetris
---
# tetris sevice
apiVersion: v1
kind: Service
metadata:
  name: tetris
  namespace: game
spec:
  selector:
    app: tetris
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8080
  type: ClusterIP
# ingress 설정
---
# ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: game
  name: ingress-game
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-2:59일부삭제6334:certificate/749846일부삭제3c9f60c
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
    alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}'
    external-dns.alpha.kubernetes.io/hostname: devxmonitor.click
spec:
  ingressClassName: alb
  rules:
    - host: main.devxmonitor.click
      http:
        paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: ssl-redirect
              port:
                name: use-annotation
        - path: /mario
          pathType: Prefix
          backend:
            service:
              name: mario
              port:
                number: 80
        - path: /tetris
          pathType: Prefix
          backend:
            service:
              name: tetris
              port:
                number: 80


2. k8s Storage

2.1. 스토리지 개요

2.2. AWS EBS CNTLRx

Amazon EBS CSI Controller는 Amazon EBS volumes의 lifecycle을 관리해주는 도구이다. EBS 볼륨의 정적/동적 할당, 볼륨 마운트(PV), 스냅샷, 사이즈 조정(PVC) 등의 기능을 수행한다 (링크)

CSI는 Container Storage Interface의 약어로써, 여러 컨테이너 오케스트레이션 환경에서 사용가능한 스토리지 플러그인 개발을 위한 업계표준으로 개발되었고(링크) 2019년 1.13 쿠버네티스에 합류하였다(링크)

Volume (ebs-csi-controller) : EBS CSI driver 동작 : 볼륨 생성 및 파드에 볼륨 연결

Amazon EBS Controller 작동과정

# [실습] PV, PVC 테스트

# 스토리지 클래스 확인
kubectl get sc kops-csi-1-21 kops-ssd-1-17

# NAME                      PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
# kops-csi-1-21 (default)   ebs.csi.aws.com         Delete          WaitForFirstConsumer   true                   6h48m
# kops-ssd-1-17             kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   true                   6h48m

kubectl describe sc kops-csi-1-21 | grep Parameters
# Parameters:            encrypted=true,type=gp3
# Parameters:            encrypted=true,type=gp2

# 워커노드의 EBS 볼륨 확인 : tag(키/값) 필터링
aws ec2 describe-volumes --filters Name=tag:k8s.io/role/node,Values=1 --query "Volumes[].{VolumeId: VolumeId, VolumeType: VolumeType, InstanceId: Attachments[0].InstanceId, State: Attachments[0].State}" | jq

# [
#   {
#     "VolumeId": "vol-0ec8349352cdbb0ba",
#     "VolumeType": "gp3",
#     "InstanceId": "i-0bea29559ca4a58d0",
#     "State": "attached"
#   },
#   {
#     "VolumeId": "vol-0c222bc833c19ba10",
#     "VolumeType": "gp3",
#     "InstanceId": "i-0992018185e1cba18",
#     "State": "attached"
#   }
# ]

# 워커노드에서 파드에 추가한 EBS 볼륨 확인
aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --query "Volumes[].{VolumeId: VolumeId, VolumeType: VolumeType, InstanceId: Attachments[0].InstanceId, State: Attachments[0].State}" | jq

# [
#     {
#       "VolumeId": "vol-0fee35f33e37f4ed1",
#       "VolumeType": "gp2",
#       "InstanceId": null,
#       "State": null
#     },
#     {
#       "VolumeId": "vol-0d430b14933648af3",
#       "VolumeType": "gp2",
#       "InstanceId": null,
#       "State": null
#     }
#   ]

EBS 볼륨 생성 테스트

2.3. AWS Volume Snapshot CNTLR

aws-ebs-csi-driverd에 포함되어있기도 한 볼륨 스냅샷 기능을 활용하여, EBS(PV)에 대한 스냅샷 생성 또는 복원이 가능하다. (링크1 링크2)

# [실습] 테스트 PVC/파드 생성

# PVC 생성
kubectl apply -f ~/pkos/3/awsebs-pvc.yaml

# 파드 생성
kubectl apply -f ~/pkos/3/awsebs-pod.yaml

# VolumeSnapshot 생성 : Create a VolumeSnapshot referencing the PersistentVolumeClaim name
cat ~/pkos/3/ebs-volume-snapshot.yaml | yh
kubectl apply -f ~/pkos/3/ebs-volume-snapshot.yaml

# 파일 내용 추가 저장 확인
kubectl exec app -- tail -f /data/out.txt

# VolumeSnapshot 확인
kubectl get volumesnapshot
NAME                  READYTOUSE   SOURCEPVC   SOURCESNAPSHOTCONTENT   RESTORESIZE   SNAPSHOTCLASS   SNAPSHOTCONTENT                                    CREATIONTIME   AGE
ebs-volume-snapshot   true         ebs-claim                           4Gi           csi-aws-vsc     snapcontent-5881b167-2375-4aa7-b0ad-c5d305cc0aaf   90s            91s

kubectl get volumesnapshot ebs-volume-snapshot -o jsonpath={.status.boundVolumeSnapshotContentName}
snapcontent-5881b167-2375-4aa7-b0ad-c5d305cc0aaf

kubectl describe volumesnapshot.snapshot.storage.k8s.io ebs-volume-snapshot

# AWS EBS 스냅샷 확인
aws ec2 describe-snapshots --owner-ids self | jq
aws ec2 describe-snapshots --owner-ids self --query 'Snapshots[]' --output table

# app & pvc 제거 : 강제로 장애 재현
kubectl delete pod app && kubectl delete pvc ebs-claim

# 스냅샷에서 PVC 로 복원
cat ~/pkos/3/ebs-snapshot-restored-claim.yaml | yh
kubectl apply -f ~/pkos/3/ebs-snapshot-restored-claim.yaml

# 확인
kubectl get pvc,pv

# 파드 생성
cat ~/pkos/3/ebs-snapshot-restored-pod.yaml | yh
kubectl apply -f ~/pkos/3/ebs-snapshot-restored-pod.yaml

# 파일 내용 저장 확인 : 파드 삭제 전까지의 저장 기록이 남아 있다. 이후 파드 재생성 후 기록도 잘 저장되고 있다
kubectl exec app -- cat /data/out.txt


3. 마무리

  • AWS VPC CNI와 다른 k8s 네트워크 플러그인들 간의 이해

쿠버네티스의 여러 네트워크 플러그인(VPC-CNI, Calico, Flannel, Weave 등)에 대해서 들어봤었지만 그 차이에 대해서 잘 알지못했었습니다. 이제 최소한 다른 플러그인 대비 VPC-CNI는 오버레이가 아닌 언더레이 구조이며, 노드 네트워크와 파드 네트워크에 동일한 CIDR 대역을 사용할 수 있다는 특징이 있음을 알았습니다.

  • 쿠버네티스 네트워크에 대해 깊이있게 파보지 못한 아쉬움
네트워크 엔지니어가 알려주는 쿠버네티스 네트워크 2편
네트워크 엔지니어가 알려주는 쿠버네티스 네트워크 2편

지난번 제 32회 네트워크 전문가 따라잡기 ‘N.EX.T’ – 정기 기술 세미나(220611) 진행시에, 현재 스터디 리더이신 가시다님의 발표를 (이해할 수 있었던 중간까지..) 재밌게 잘 들었었습니다.

그래서 2주차 과제물을 작성할때, iptables와 ipamd에 대해서 조금 더 깊이있게 파보고 싶어서 자료 정리하던중에 회사일이 바빠 그럴만한 여력이 없었다는게 아쉽습니다. 다음기회에..

  • iptables
    • 도대체 클러스터에서 파드간 / 외부와 통신할때 워커 노드에선 어떤 일이 일어나는가
    • 본문 1.3.과 1.4. 부분을 노드 관점에서 좀 더 자세히 이해헤볼것
  • ipamd
    • IP Address Management (IPAM) daemon
    • AWS VPC-CNI를 구성하는 2 구성요소 중 하나 (CNI plugin, ipamd)
    • 서브넷 대역에서 도대체 ENI는 어떻게/무슨 기준으로 비어있는 IP를 선정해서 가져오는건지?
Amazon VPC CNI 플러그인으로 노드당 파드수 제한 늘리기, YongTrans

Leave a Reply