[Kubernetes] 쿠버네티스 서비스(Service) Deep Dive - (2) ClusterIP
리마인드 - 서비스 개념
(1) Pod는 고유한 IP주소가 존재하며 일시적인(ephemeral) 특성을 지니며 불안정한 리소스이다.
(2) 클라이언트 관점에서 서비스를 지원하는 Pod의 수와 IP에 상관하지 않아야 하며 모든 Pod는 단일 IP주소로 Access할 수 있어야 한다.
(3) 쿠버네티스는 이러한 문제점을 해결하기 위해 "Service" 리소스 유형을 제공한다.
(4) Service는 어떤 파드가 어디에서 존재하는지 (어떤 IP를 가지는지)에 관계없이 Pod 앞단에서 Pod 중 하나로 연결하여 요청을 처리하도록 한다.
(5) 서비스를 지원하는 Pod는 여러개일 수 있으며, 레이블 셀렉터(Label Selector)를 통해 서비스와 연결된 Pod를 정의한다.
리마인드 - 서비스 종류
① Cluster IP 타입
쿠버네티스 내부에서만 포드에서 접근할 때 사용한다. 외부로 Pod를 노출하지 않기 때문에 쿠버네티스 클러스터 내부에서만 사용되는 Pod에 적합하다.
② NodePort 타입
Pod에 접근할 수 있는 포트를 Cluster의 모든 Node에 동일하게 개방한다. 따라서 외부에 Pod에 접근할 수 있는 서비스 타입이다. 접근할 수 있는 포트는 랜덤으로 정해지지만 특정 포트로 접근하도록 설정할 수도 있다.
③ LoadBalancer 타입
클라우드 플랫폼에서 제공하는 Load Balancer를 동적으로 Provisioning하여 포드에 연결한다. NodePort 타입과 마찬가지로 외부에서 포드에 접근할 수 있는 서비스 타입이다. 일반적으로 AWS, GCP 등과 같은 클라우드 플랫폼 환경에서만 이용할 수 있다.
목표 - Architecture
각 서비스 타입별 (ClusterIP, NodePort, LoadBalancer) 실습 및 서비스 특성 파악
사전준비
앞서, 실습환경을 구성하자.
① gitclone https://github.com/luksa/kubernetes-in-action-2nd-edition.git
② kubectl apply -f kubernetes-in-action-2nd-edition/Chapter11/SETUP/quote
③ PV, PVC, POD (더보기를 클릭 후 yaml파일 참조하여 각 오브젝트 생성)
pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: quiz-data
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
- ReadOnlyMany
hostPath:
path: /var/quiz-data
storageClassName: ""
pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: quiz-data
spec:
resources:
requests:
storage: 5Gi
accessModes:
- ReadWriteOnce
storageClassName: ""
volumeName: quiz-data
pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: quiz
labels:
app: quiz
rel: stable
spec:
volumes:
- name: initdb
emptyDir: {}
- name: quiz-data
persistentVolumeClaim:
claimName: quiz-data
initContainers:
- name: installer
image: luksa/quiz-initdb-script-installer:0.1
imagePullPolicy: Always
volumeMounts:
- name: initdb
mountPath: /initdb.d
containers:
- name: quiz-api
image: luksa/quiz-api:0.1
imagePullPolicy: Always
ports:
- name: http
containerPort: 8080
- name: mongo
image: mongo:4.4.6
volumeMounts:
- name: quiz-data
mountPath: /data/db
- name: initdb
mountPath: /docker-entrypoint-initdb.d/
readOnly: true
사전 준비 완료 시 아래와 같이 구성이 된다.
1. ClusterIP
(1) ClusterIP는 서비스 리소스의 가장 기본이 되는 타입이다. 별다른 타입 지정을 하지 않으면 기본적으로 ClusterIP로 설정된다.
(2) 외부에서는 접근할 수 없으며 쿠버네티스 내부에서만 Pod에서 접근할 때 사용한다.
ClusterIP 사용 목적
외부와 통신이 불가능한데 굳이 왜 ClusterIP를 사용할까? 이유는 크게 2가지로 나뉜다.
첫째, 네트워크 보안 및 관리 목적
일반적으로 보안상 한 두개의 외부 서비스 외에 직접적으로 트래픽을 전달받는 경우는 드물다. 열린 서비스 끝점으로부터 트래픽을 전달받아 내부 파드와 소통하는데 이용한다.
둘째, 복잡한 네트워킹 수행 가능
쿠버네티스 서비스를 정의하는 메니페스트 파일을 통해 보다 정교하게 네트워킹을 정의하고 수행할 수 있다.
ClusterIP 서비스 생성
quote Pod 대상 manifest 파일을 생성하여 서비스를 생성해보자.
apiVersion: v1
kind: Service
metadata:
name: quote
spec:
type: ClusterIP
selector:
app: quote
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
아래와 같이 yaml 파일을 작성하고 kubectl apply 명령어를 통해 서비스를 생성하자.
① quote라는 서비스 이름으로 정의하며 포트 80의 연결을 허용한다.
② 각 연결을 포트 80으로 레이블 app=quote 와 일치하는 Pod로 포워딩한다.
Quiz Pod에 대해서 명령형 커맨드(Imperative Command)를 통해 서비스를 생성해 보자.
kubectl expose pod quiz --name quiz
여기서 quiz 파드에 적용된 레이블 app=quiz, rel=stable이 적용된 것을 확인할 수 있다. 그러나, quiz라는 서비스가 stable 레이블이 적용된 것이 아닌 조금 더 광범위적으로 적용하는 상황을 고려하여 app=quiz 레이블만 적용할 수 있도록 정책을 수정해보자.
# 레이블 설렉터를 app=quiz로 설정하도록 변경
root@k8s-m:~# kubectl set selector service quiz app=quiz
service/quiz selector updated
# 정책 반영 확인
root@k8s-m:~# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
quiz ClusterIP 10.104.206.158 <none> 80/TCP 4m51s app=quiz
quote ClusterIP 10.97.190.49 <none> 80/TCP 5m42s app=quote
추가적으로, 이미 생성한 서비스에서 포트 수정도 가능하다.
kubectl edit 기능을 활용하여 8080:8080 을 80:8080으로 수정해보자.
kubectl edit svc quiz
생성한 서비스 확인
service를 svc로 줄여서 쓸 수 있다.
root@k8s-m:~# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
quiz ClusterIP 10.104.206.158 <none> 80/TCP 4m51s app=quiz
quote ClusterIP 10.97.190.49 <none> 80/TCP 5m42s app=quote
Pod로부터 Service 접근
이제 quote-001 파드로부터 일전에 생성한 quote, quiz 서비스에 대하여 접근이 가능한지 시도해보자.
나의 경우, quiz 서비스 ClusterIP는 10.104.206.158 quote 서비스 ClusterIP는 10.97.190.49 이다.
root@k8s-m:~# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
quiz ClusterIP 10.104.206.158 <none> 80/TCP 4m51s app=quiz
quote ClusterIP 10.97.190.49 <none> 80/TCP 5m42s app=quote
우선, quote-001 내부에서 Shell을 실행시켜보자. 그리고, quote-001로부터 quiz, quote 서비스에 접속이 되는지 확인해보자.
#파드 내부 접속
kubectl exec -it quote-001 -c nginx -- sh
/ #
# quote-001(Pod) → quiz(Service) 접속
/ # curl http://10.104.206.158
This is the quiz service running in pod quiz
# quote-001(Pod) → quote(Service) 접속
/ # curl http://10.97.190.49
This is the quote service running in pod quote-003 on node k8s-w3
파드가 여러개인 경우, 서비스가 로드밸런서와 같이 다른 Pod들에 여러번 접근하는 것을 확인할 수 있다.
/ # while true; do curl http://10.97.190.49; done
This is the quote service running in pod quote-001 on node k8s-w2
This is the quote service running in pod quote-002 on node k8s-w1
This is the quote service running in pod quote-001 on node k8s-w2
This is the quote service running in pod quote-003 on node k8s-w3
This is the quote service running in pod quote-003 on node k8s-w3
This is the quote service running in pod quote-canary on node k8s-w3
This is the quote service running in pod quote-002 on node k8s-w1
💡 기본적으로 서비스는 트래픽을 랜덤하게 파드로 분산시킨다. 만약, 특정 클라이언트와 특정 Pod를 지속적으로 연결이 되게 하려면 spec.sessionAffinity를 통해 구현할 수 있다.
SessionAffinity 타입은 None, Client IP 2가지만 지원한다. (None은 랜덤하게 파드로 로드밸런싱 한다.)
참고
- kubernetes-2nd-edition
'Engineering > Kubernetes (K8S)' 카테고리의 다른 글
[Kubernetes] 쿠버네티스 서비스(Service) Deep Dive - (4) Nodeport (0) | 2022.06.08 |
---|---|
[Kubernetes] 쿠버네티스 서비스(Service) Deep Dive - (3) Cluster DNS (0) | 2022.05.18 |
[Kubernetes] 쿠버네티스 클러스터 업그레이드 (v1.21 → v1.22) (0) | 2022.02.08 |
[Kubernetes] 쿠버네티스 서비스(Service) - Deep Dive -(1) (0) | 2021.12.26 |
[Kubernetes] 쿠버네티스 레이블 & 어노테이션 (Label, Annotation) - Deep Dive (0) | 2021.12.13 |
댓글