*본 게시글의 내용은 가시다님의 노션 페이지와 스터디 자료인 '24단계 실습으로 정복하는 쿠버네티스 도서' 를 기반으로 작성하였습니다.
AWS VPC CNI
)
- AWS 환경에서 동작하는 Kops나 EKS를 위한 CNI
- 온프레미스에 k8s를 구성하면 노드(호스트)의 네트워크 대역과 파드의 네트워크 대역이 다르다.
- AWS VPC CNI는 노드와 파드가 같은 네트워크 대역을 사용하며, 각 파드에 eni가 연결되어 독립적인 IP를 할당받아 사용할 수 있다.
- 단, 파드의 개수가 많아지면 많아질수록 각 서브넷에서 가용한 IP의 수가 그만큼 소모되고, 노드의 역할을 하는 EC2가 가질 수 있는 ENI당 Secondary IP의 리밋도 있기 때문에 사전에 고려가 필요하다.
- 단, Kops는 아직 PodSecurityGroup을 지원하지는 않는다. 현재는 EKS에서만 가능.
- kube-proxy와 aws-node 파드 등 몇몇 파드는 노드와 동일한 IP를 가진다. 때문에 ENI Secondary IP 갯수를 계산할 때 포함하지 않는다.
[root@kops-ec2 ~]# kubectl get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
i-01493969e37859769 Ready control-plane 35m v1.24.11 172.30.42.219 3.36.54.165 Ubuntu 20.04.5 LTS 5.15.0-1031-aws containerd://1.6.18
i-0e56757db751fb4e9 Ready node 32m v1.24.11 172.30.63.228 13.209.10.83 Ubuntu 20.04.5 LTS 5.15.0-1031-aws containerd://1.6.18
[root@kops-ec2 ~]kubectl get pod -o wide -A
...
kube-system aws-node-gf6hx 1/1 Running 0 36m 172.30.42.219 i-01493969e37859769 <none> <none>
kube-system aws-node-vxf5r 1/1 Running 0 34m 172.30.63.228 i-0e56757db751fb4e9 <none> <none>
...
kube-system kube-proxy-i-01493969e37859769 1/1 Running 0 35m 172.30.42.219 i-01493969e37859769 <none> <none>
kube-system kube-proxy-i-0e56757db751fb4e9 1/1 Running 0 34m 172.30.63.228 i-0e56757db751fb4e9 <none> <none>
...
...
node의 IP 라우팅 정보를 보면, veth 인터페이스로 걸려있는 IP들이 VPC CNI에 물려있는 파드의 IP이다.
ubuntu@i-01493969e37859769:~$ ip -c route
default via 172.30.32.1 dev ens5 proto dhcp src 172.30.42.219 metric 100
172.30.32.0/19 dev ens5 proto kernel scope link src 172.30.42.219
172.30.32.1 dev ens5 proto dhcp scope link src 172.30.42.219 metric 100
172.30.60.80 dev enif9dc7364ce2 scope link
172.30.60.81 dev eni4d9edc11021 scope link
172.30.60.82 dev enia699f5ca146 scope link
172.30.60.83 dev eni35b0ddd5b59 scope link
---
[root@kops-ec2 ~]# kubectl get pod -A -o wide | grep "172.30.60.80"
kube-system ebs-csi-node-xzdbn 3/3 Running 0 40m 172.30.60.80 i-01493969e37859769 <none> <none>
파드를 생성하면, 파드가 배치된 노드의 라우팅 정보에서도 추가된 파드의 IP를 확인할 수 있다.
(ilikebigmac:default) [root@kops-ec2 ~]# kubectl get pod -o wide
columns=NAME:.metadata.name,IP:.status.podIPNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
netshoot-pod-7757d5dd99-cvggz 1/1 Running 0 88s 172.30.57.181 i-0e56757db751fb4e9 <none> <none>
netshoot-pod-7757d5dd99-r4lnj 1/1 Running 0 88s 172.30.57.182 i-0e56757db751fb4e9 <none> <none>
생성된 파드 내에서 exec으로 접근해도 같은 172.30.57.181 IP를 확인할 수 있다.
netshoot-pod-7757d5dd99-cvggz# ip -c route
default via 169.254.1.1 dev eth0
169.254.1.1 dev eth0 scope link
netshoot-pod-7757d5dd99-cvggz# ip -c addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
3: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
link/ether 7e:9b:0d:58:45:f9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.30.57.181/32 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::7c9b:dff:fe58:45f9/64 scope link
valid_lft forever preferred_lft forever
파드간 통신
확인해 본 바와 같이 AWS VPC CNI를 이용하면 파드는 노드와 같은 IP 대역을 할당받게 된다.
노드는 EC2로 구성되어 있으므로 노드와 파드는 동일한 VPC 내에 위치한다는 의미이다.
그리고 VPC의 대역은 앞서 확인한 파드와 노드의 IP 대역인 172.30으로 시작하는 대역이다.
따라서 서로 같은 VPC 내에 위치한 모든 파드들은 NAT를 통하지 않더라도 VPC 대역에 해당하는 Private IP로 통신을 할 수 있게 된다.
파드의 외부 네트워크 통신
netshoot-pod-7757d5dd99-cvggz# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=104 time=22.7 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=104 time=22.6 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=104 time=22.6 ms
파드 내에서 구글 DNS로 ping을 날려보면 정상적으로 응답을 주고받을 수 있다. 그렇다면 외부 통신은 어떻게 될까?
ubuntu@i-0e56757db751fb4e9:~$ 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.63.228 --random-fully
워커 노드에서 확인해보면, 파드가 외부와 통신할 때는 AWS-SNAT-CHAIN 에 의해 EC2의 Public IP로 SNAT되어서 외부와 통신하게 된다.
k8s Service & AWS LoadBalancer Controller
AWS LoadBalancer Controller는 ELB의 Target Group에 타겟으로 연결된 Pod의 정보를 지속적으로 제공하는 역할을 한다.
k8s Service를 LoadBalancer Type으로 생성하면 타겟을 Instance와 IP 타입 중 선택할 수 있는데, IP 타입으로 구성하게 되면 ELB는 LoadBalancer Controller에 의해 Pod의 IP를 동적으로 제공받게 된다.
따라서 Client-ELB-Pod로 효율적인 네트워크를 구성할 수 있다. 만약 Instance Type을 선택하게 되면 기존에 사용하던 NodePort 방식으로 구성된다.
AWS LoadBalancer Conteroller Docs
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-websr
AWS LoadBalancer Conterller Annotations
Service metadata에 기입된 annotations는 어떤 방식으로 ELB에 서비스와 파드를 매핑하여 구성할 것인지 정의하게 된다.
위 링크에서 NLB/ALB의 각기 다른 annotation을 확인할 수 있다.
위의 서비스는 NLB에 ip타입으로 매핑하며 cross zone load balancing 옵션을 enable 하고, internt-facing 이므로 외부에서 브라우저로 접근해도 위와 같이 서비스에 접근이 가능하다.
이와 같은 구성으로 AWS 환경에 NLB가 생성된다.
internet-facing이므로 일반 환경에서도 생성된 NLB의 DNS Name으로 접속해보면 위와 같은 결과를 확인할 수 있다.
연결된 타겟그룹에는 172.30.57.181, 172.30.57.182 두 개의 IP와 8080포트로 매핑되어 있다.
(ilikebigmac:default) [root@kops-ec2 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deploy-echo-5c4856dfd6-bhvjm 1/1 Running 0 8m25s 172.30.57.181 i-0e56757db751fb4e9 <none> <none>
deploy-echo-5c4856dfd6-g7vht 1/1 Running 0 8m25s 172.30.57.182 i-0e56757db751fb4e9 <none> <none>
그리고 이들 IP는 앞서 echo-server deployment에 의해 배포된 파드의 IP임을 확인 가능하다.
Ingress
인그레스는 클러스터 내부의 서비스(ClusterIP, NodePort, Loadbalancer)를 HTTP/HTTPS로 외부에 노출시키는 Web Proxy의 역할을 한다.
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
마찬가지로 annotation에 의해 ip타입의 internet-facing ALB가 AWS 환경에 생성된다.
[root@kops-ec2 ~]# kubectl get pod -n game-2048 -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deployment-2048-6bc9fd6bf5-c64wr 1/1 Running 0 4m14s 172.30.55.145 i-0e56757db751fb4e9 <none> <none>
deployment-2048-6bc9fd6bf5-ks2v4 1/1 Running 0 4m14s 172.30.55.144 i-0e56757db751fb4e9 <none> <none>
타겟 역시 IP타입으로 매핑되어 파드의 IP와 동일하며, 80포트로 매핑되어 있다.
ALB의 DNS Name으로 접근하면 호스팅 중인 웹 서비스에 접근이 가능하다.
이와 같은 웹 서비스를 배포하는 상황을 가정하고, 생성된 ALB에 ExternalDNS를 albweb.ilikebigamc.link
로 설정하였으며, 정상 접근됨을 확인하였다.
DNS 레코드와 등록 정보를 살펴보면
(ilikebigmac:default) [root@kops-ec2 ~]# curl albweb.ilikebigmac.link -vvv
* Trying 3.39.77.155:80...
* Connected to albweb.ilikebigmac.link (3.39.77.155) port 80 (#0)
> GET / HTTP/1.1
> Host: albweb.ilikebigmac.link
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sat, 18 Mar 2023 20:20:08 GMT
< Content-Type: text/html
< Content-Length: 3988
< Connection: keep-alive
< Server: nginx
< Last-Modified: Wed, 06 Oct 2021 17:35:37 GMT
< ETag: "615dde69-f94"
< Accept-Ranges: bytes
<
...
* Connection #0 to host albweb.ilikebigmac.link left intac
(ilikebigmac:default) [root@kops-ec2 ~]# dig albweb.ilikebigmac.link
; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.amzn2.5.2 <<>> albweb.ilikebigmac.link
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 36674
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;albweb.ilikebigmac.link. IN A
;; ANSWER SECTION:
albweb.ilikebigmac.link. 21 IN A 3.39.208.51
albweb.ilikebigmac.link. 21 IN A 3.39.77.155
;; Query time: 0 msec
;; SERVER: 10.0.0.2#53(10.0.0.2)
;; WHEN: Sun Mar 19 05:20:41 KST 2023
;; MSG SIZE rcvd: 84
3.39.208.51
과 3.39.77.155
라는 두 개의 IP를 돌려주는데 이를 ENI에서 조회해보면 ALB의 Public IP임을 알 수 있다.
따라서 지금까지 확인한 내용을 바탕으로 `albweb.ilikebigmac.link` 에 접속할 때 클라이언트는 Route53(External DNS) -> ALB -> Pod의 흐름으로 페이지에 접근하게 됨을 알 수 있었다.
이 때 Route53에 등록되는 도메인은 ExternalDNS에 의헤 레코드 관리되고, LoadBalancer Controller는 동적으로 변화하는 Pod의 IP를 지속적으로 ELB에 제공한다.
ELB는 k8s Service 명세의 annotation에 매핑된 정보를 바탕으로 생성한 ELB에 타겟 그룹을 연결, 타겟 그룹에는 전달받은 Pod의 IP/Port 가 타겟으로 등록된다.
EKS에서 VPC CNI를 사용하는 경우 이러한 구조 덕에Pod에 직접 보안 그룹을 적용할 수 있으며, Node와 Pod는 동일 네트워크 대역을 가지고 이 대역은 VPC CIDR에 해당한다.
각 Pod의 IP는 ENI의 Secondary IP로 등록되는데, EC2의 Instance Type에 따라 등록될 수 있는 Pod IP의 갯수에 제한이 있고, 각 서브넷에서도 available IP에 제한이 있으므로 인스턴스별, 서브넷별로 배치할 파드의 갯수가 적절한지 배포 전에 검증해볼 필요가 있다.
'Kubernetes > PKOS' 카테고리의 다른 글
[보완] 쿠버네티스 스토리지와 CSI Driver (0) | 2023.04.08 |
---|---|
[보완] kubernetes 워커노드를 spot instance로 구성하기(Node Termination Handler) (1) | 2023.04.02 |
[4주차] 쿠버네티스 모니터링 (0) | 2023.04.02 |
[3주차] GitOps와 ArgoCD (0) | 2023.03.26 |
[1주차]Kops 를 이용하여 AWS에 쿠버네티스 클러스터 구축하기 (2) | 2023.03.12 |