'15단계로 배우는 도커와 쿠버네티스' 기반으로 내용 정리하였습니다.
파드는 일시적인 존재라 언제든지 할당된 IP 주소가 바뀔 수 있다. ( 젤 중요한 파드의 특징!! )
클라이언트 입장에서는 늘 변하는 파드의 IP 주소를 알기 어렵기 때문에 쿠버네티스에는 서비스라는 오브젝트가 존재한다.
서비스 타입 개요
( 플로는 LoadBalancer 타입을 사용하겠지..? )
각 서비스 타입의 특징에 대해 알아보자!
ClusterIp
서비스 타입을 지정하지 않으면 기본 값으로 ClusterIP 로 만들어진다.
그림과 같이 클러스트 내부에서 내부 DNS에 등록한 이름으로 특정 파드 집합에 요청을 전송할 수 있게 해준다.
ClusterIP 는 다음 용도로 사용될 수 있다.
매니페스트에 'clusterIP : None' 이라고 지정하면, 헤드리스 설정으로 서비스가 동작한다.
이 설정에서는 대표 IP 주소를 획득하지 않고, 부하분산도 이뤄지지 않는다.
그 대신에 파드들의 IP 주소를 내부에 등록하여, 파드의 IP 주소 변경에 대응하여 최신 상태를 유지한다.
(그럼 대표 IP가 없고 서비스 이름을 통해 요청이 들어오면 서비스가 알아서 요청을 파드에 보낼 수 있는 건가??)
서비스 타입 NodePort
서비스 타입에 NodePort 를 지정하면, 앞서 설명한 ClusterIP 의 기능에 더해 노드의 IP 주소에 공개 포트가 열린다.
이를 통해 K8s 클러스터 외부에서 내부의 파드에 요청을 보낼 수 있게 된다.
공개 포트번호의 범위는 기본적으로 30000 - 32767 이다.
클라이언트가 노드의 IP와 포트로 전송한 요청은 최종적으로 파드에 전달된다.
NodePort 타입의 서비스를 만들면 클러스터의 모든 노드에 지정한 포트가 열리게 된다.
그리고 각 노드가 수령한 요청은 대상이 되는 파드들에게 부하분산되어 전송된다.
이때 요청을 받은 노드 내에 있는 파드로만 전송하도록 설정할 수 있다.
노드들 앞에 로드 밸런서가 있다면 매우 유용한 설정이다.
( 서비스 타입이 loadbalancer 인 서비스가 아닌 그냥 로드밸런서겠지..? )
여기서 한 가지 주의점이 있다.
사용자가 특정 노드를 지정해서 접속하고 있는데 해당 노드가 하드웨어 점검 등의 이유로 셧다운 된다면 서비스를 이용할 수 없게 된다.
그리고 이미 사용 중인 포트를 설정하면 매니페스트 배포 과정에서 실패한다.
NodePort는 쉽고 편리하게 설정할 수 있지만 정식 서비스에서 사용하는 것은 추천하지 않는다.
( 외부 통신이 가능하지만... 흠 안정적이지 않은 느낌임. )
서비스 타입 LoadBalancer
로드밸런서와 연동하여 파드의 애플리케이션을 외부에 공개한다.
또한, LoadBalancer는 NodePort를 사용하기 때문에 ClusterIP 도 자동적으로 만들어진다.
서비스 타입 ExternalName
파드에서 K8s 클러스터 외부의 엔드포인트에 접속하기 위한 이름을 해결해 준다.
예를 들어, 퍼블릭 클라우드의 데이터 베이스나 인공지능 API 서비스 등을 접근할 때 사용될 수 있다.
ExternalName은 서비스의 이름과 외부 DNS 이름의 매핑을 내부 DNS에 설정한다.
이를 통해 파드는 서비스의 이름으로 외부 네트워크의 엔드포인트에 접근할 수 있다.
이떄 포트 번호까지는 지정할 수 없다.
이 서비스 타입은 파드에서 외부의 엔드포인트에 접속할 때 편리하다.( db 서버 )
네임스페이스에서의 서비스 이름으로 ip 주소를 얻을 수 있기 때문이다.
또한, K8s 클러스터 내의 서비스로 교체하기도 쉽다.
서비스와 파드의 연결
서비스가 요청을 전송할 파드를 결정할 때는 실렉터의 라벨과 일치하는 파드를 etcd로 부터 선택한다고 설명했다.
여기서는 서비스의 매니페스트에 기술하는 실렉터와 매니페스트에 기술하는 라벨을 구체적으로 비교해보겠다.
[ 서비스 YAML ]
apiVersion: v1
kind: Service
metadata:
name: hello-node-svc
spec:
selector:
app: hello-node
ports:
- port: 80
protocol: TCP
targetPort: 8080
type: LoadBalancer
[ 디플로이먼트 YAML ]
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deploy
spec:
replicas: 3
selector:
matchLabels
app: hello-node
template:
metadata:
label:
app: hello-node
spec:
containers:
- name: nginx
image: nginx:latest
서비스의 selector 와 디플로이먼트 파드 템플릿의 'metadata.label' 에 같은 라벨 'app:hello-node'를 기술한다.
이러한 설정에 의해 서비스의 요청 전송 파드가 결정된다.
하나의 파드 템플릿으로 만들어지는 파드들은 같은 속성이 부여되므로 라벨도 같다.
따라서 디플로이먼트에 의해 만들어지는 파드들은 같은 라벨을 가지게 되어 서비스의 요청을 전송받는다.
이와 같은 방식은 selector의 값을 바꾸는 것만으로 서비스가 전송하는 파드의 그룹을 바꿀 수 있어 운영상의 유연성을 가질 수 있다.
또 중복에 주의하여 의도하지 않는 파드로 요청이 전송되지 않도록 주의해야 한다.
서비스의 매니페스트 작성법
apiVersion: v1
kind: Service
metadata:
name: hello-node-svc
spec:
selector:
app: hello-node
ports:
- port: 80
protocol: TCP
targetPort: 8080
type: LoadBalancer
[ 서비스 API ]
[ 서비스 사양 ]
[ 서비스 파드 사양 ]
[ 세션 어피니티 ]
동일한 클라이언트에서 온 요청은 언제나 같은 파드에 전송하고 싶을 수 있다.
그런 경우에는 매니페스트의 세션 어피니티를 ClientIP 로 설정하면 된다.
마무리
서비스는 클라이언트의 요청을 파드에 전달하기 위한 오브젝트로서 부하분산 기능을 포함한다.
서비스 타입에 따라 공개 방법과 범위가 다르다.
서비스 타입 ClusterIP를 사용하면 k8s 클러스터 내부에서 접속할 수 있으며, NodePort를 사용하면 K8s 클러스터 외부에서 노드의 IP 주소와 포트번호로 접속할 수 있다. 그리고 LoadBalancer 를 사용하면 K8s 클러스터 외부에서 대표 IP 주소로 접속할 수 있다.
서비스 타입은 ExternalName 은 K8s 클러스터 외부의 DNS 이름을 서비스 이름으로 등록하다. 서비스 명과 IP 주소를 매핑하고 싶은 경우에는 헤드리스 서비스를 검토해야 한다.
서비스가 받은 요청을 전달할 파드는 라벨에 의해 결정된다.
서비스의 부하분산 알고리즘은 기본적으로 랜덤이며, 세션 어피니티에 의해 클라이언트의 IP 주소별로 전송될 파드를 고정할 수 있다.
서비스에는 HTTP 헤더의 쿠키에 의한 세션 어피니트 기능은 없다. (?! 쿠키를 고정하는 기능인가? )
해당 기능이 필요한 경우에는 인그레스 사용을 검토한다.
NodePort를 사용해도 외부에 애플리케이션을 공개할 수 있지만 가용성이 부족하여 정식 서비스에 적합하지 않다. 반면 LoadBalancer는 가용성도 있고 HTTP와 HTTPS를 사용할 수 있어 정식 서비스에 적합하다.