Amazon EKS 관리형 서비스는 컨트롤 플레인을 직접 구성하지 않고서 k8s를 손쉽게 사용할 수 있도록 편리함을 제공
AWS에서 제공하는 VPC, ELB, IAM 등 특정 기능들을 같이 활용하고자 할 때 유용
3가지의 스토리지 케이스가 있음
1. 컨테이너의 볼륨
1,2는 호스트의 가용성에 의존되어 있음.
Stateless 애플리케이션에 적합
Stateful 애플리케이션은 Persistent 볼륨이 필요
PersistentVolume(PV)
PersistentVolumeClaim(PVC)
Provisioning
Container Network Interface의 약어로 쿠버네티스 네트워크에 오버레이 네트워크를 구성해주고 파드와 호스트 인터페이스를 연결해주는 부분을 별도의 모듈로 분리한 플러그인이다.
결론: kube-proxy의 netfilter에 정의되어있는 chain rule에 의하여 요청을 포워딩한다.
해당 네트워크 방식을 이해하기 전에 Service란 리소스에 대해서 알아보고 네트워크 구조와 동작방식에 대해서도 알아보자.
쿠버네티스는 기본적으로 Pod는 쉽게 대체될 수 있는 존재이기 때문에 IP로 Service와 통신하기에는 부적절하다. 따라서 Pod앞단에 Reverse Proxy를 위치시키는 방법이있다. 이 Reverse Proxy를 수행해주는 추상적인 리소스가 Service이다. Service는 쿠버네티스의 리소스타입 중 하나로써 각 Pod로 트래픽을 포워딩해주는 프록시 역할을한다. Pod 네트워크와 동일하게 Service 네트워크 또한 가상 IP주소이다.
즉, 쿠버네티스 클러스터 내부에서 통신할 때는 이 Service의 IP를 거쳐서 통신하게 된다.
위의 그림처럼 두 개의 워커노드가 있고 하나의 게이트웨이를 통해 서로 연결되어 있다. 또한 마스터 노드에서 아래의 명세를 통해 service 리소스를 생성했다고 가정한다.
apiVersion: v1
kind: Service
metadata:
name: was-svc # service의 이름
spec:
selector:
app: was # was Pod의 라벨
type: ClusterIP # service의 type 설정
ports:
- protocol: TCP
port: 80 # service에서 서버 컨테이너 어플리케이션과 매핑시킬 포트 번호
targetPort: 8080 # 서버 컨테이너에서 구동되고 있는 서버 어플리케이션 포트 번호
$ kubectl create -f svc-default.yml
위의 그림에서 Web Pod가 WAS의 자원을 요청받는 과정을 요약해보면 다음과 같다.
1. Web Pod가 was-svc에 자원을 요청
2. CoreDNS가 해당 서비스 이름(was-svc)을 service의 IP(10.3.241.152)로 매핑시켜줌
3. Web Pod는 Service로 (10.3.241.152:80)으로 패킷요청
4. veth1는 해당 서비스의 IP를 모름. 따라서 상위 게이트웨이로 패킷전달
5. cni0도 해당 서비스의 IP를 모름. 따라서 상위 게이트웨이로 패킷을 전달
6. eth0도 해당 서비스의 IP를 모름. 따라서 상위 게이트웨이로 패킷을 전달해야 하지만..
- 여기서 kube-proxy의 netfilter에 정의되어 있는 Chain Rule에 의하여 요청을 포워딩하게됨.
7. 라우터에 정의되어 있는 라우팅 테이블에 의해 Worker Node2로 포워딩
8. eth1의 kube-proxy에서 netfilter가 패킷을 Listening 중 이고 패킷이 들어오면 cni1로 포워딩 해줌
9. cni1은 단순 브릿지 네트워크이므로 하위 계층으로 패킷을 넘김
10. 패킷은 server-svc에 도달하고 매핑된 Pod에 패킷을 넘긴다.
IP는 (Layer 3)는 기본적으로 자신의 Host에서 목적지를 찾지 못하면 상위 게이트웨이로 패킷을 전달한다. 위의 예시에서 보면(5번) client pod는 10.3.241.152 의 위치를 모르기 때문에 보통이라면 최상위에 존재하는 게이트웨이로 전달될 것이지만. kube-proxy라는 컴포넌트를 통해 패킷흐름이 제어가 된다.
kube-proxy는 iptables를 이용하여 chain rule 규칙을 지정하고 패킷을 프록싱하도록 네트워크를 설정한다.
serviceIP를 발견하고 그것을 실제 Pod로 전달하는 것은 모두 netfilter가 담당하게 되었고 kube-proxy는 단순히 netfilter의 규칙을 알맞게 수정하는것을 담당할 뿐이다.
Pod를 생성하게 되면 kube-proxy에서 동적으로 netfilter에 chain rule규칙을 생성한다. 이 규칙은 파드가 생성된 노드에 ssh로 접속 후 iptables -t nat -L 명령어로 확인가능하다.
ervice 네트워크는 Service가 할당받는 네트워크 인터페이스이다.
모든 service 는 Cluster-IP라는 IP 주소를 부여받고 클러스터 내부적으로 이 IP주소를 통해 자신이 포워딩 해야 할 Pod들에게 트래픽을 전달한다.
즉, 기본적으로 service는 클러스터 내부적으로만 통신할 수 있게끔 설계 되어 있다. 그렇다면 외부와 통신은 어떻게 하는것인가?
service는 여러가지 타입을 통해 외부통신을 가능하게끔 기능을 제공한다.
NodePort타입의 서비스를 생성하게 되면 kube-proxy가 모든 노드의 eth0 네트워크 인터페이스에 30000-32767 포트 사이의 임의의 포트를 할당한다. 할당된 포트로 요청이 들어오게 되면 이것을 매핑된 ClusterIP(=service)로 전달한다.
NodePort를 생성하면 쿠버네티스 내부적으로 아래의 변경사항이 발생한다.
로드밸런서는 기본적으로 외부 클라우드 서비스를 사용하여 로드밸런서를 프로비저닝 할 수 있는 경우에만 사용할 수 있는 서비스 타입이다.
만약 베어메탈 환경에서 로드밸런서를 사용을 하고싶다면 metal-lb라는 플러그인을 따로 설치를 해줘야 사용을 할 수 있게된다.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
clusterIP: 10.0.171.239
type: LoadBalancer
status:
loadBalancer: # 여기에 로드밸런서에 대한 내용 추가
ingress:
- ip: 192.0.2.127
로드밸런서 서비스 타입은 TLS termination 설정이 불가능하고 URL-Path 라우팅이 불가능하다. 그렇기 때문에 한개의 로드밸런서를 이용하여 여러 서비스에 연결하는 것은 불가능하다.
반면 Ingress 서비스 타입은 리버스 프록시를 통해서 클러스터 내부의 Service로 어떻게 포워딩 시킬 것인지 명시한 리소스이다. 로드밸런서와는 달리 TLS termination이나 URL-Path 라우팅을 가능하게 한다.
Ingress는 리소스 타입과 그 리소스 타입을 관리하는 Ingress-Controller가 존재한다. 아래의 코드가 Ingress 리소스 예시이다.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
annotations:
kubernetes.io/ingress.class: "gce"
spec:
tls:
- secretName: my-ssl-secret
rules:
- host: testhost.com
http:
paths:
- path: /*
backend:
serviceName: service-test
servicePort: 80
Ingress-Controller는 요청을 적절한 서비스로 전달해야 하는 역할을 담당한다. Ingress를 사용할 때, 요청 받을 서비스를 NodePort 타입으로 설정을 하고 Ingress-controller로 하여금 어떻게 요청을 각 노드에 전달할지 파악하게 한다. 각 클라우드 플랫폼 마다의 Ingress-controller 구현체가 있다.