Overview
이전 글에서 이론적으로 학습한 Kubernetes의 Service 오브젝트에 대해, 이번에는 실습을 통해 직접 적용해보는 시간을 가졌다.
Pod를 외부 또는 클러스터 내에서 접근 가능한 방식으로 노출하는 다양한 Service 타입 중,
ClusterIP, NodePort, LoadBalancer 를 중심으로 구성하고 각각의 동작 방식과 네트워크 흐름을 확인했다.
실습은 사내의 3대 VM 환경(dh-k8s-master, dh-k8s-node, dh-k8s-node2)에서 진행되었으며,
실제 요청을 curl 명령어로 수행하면서 서비스 유형에 따른 트래픽 분산, 접근 범위,
그리고 externalTrafficPolicy에 따른 트래픽 라우팅의 차이를 직접 확인해볼 수 있었다.
2022.08.29 - [교육, 커뮤니티 후기] - <인프런> 대세는 쿠버네티스 [초급] - No.9 Object - Service
<인프런> 대세는 쿠버네티스 [초급] - No.9 Object - Service
Overview 오늘은 kubernetes service에 대해서 공부해보려고 한다. Object - Service ClusterIP service는 기본적으로 자신의 ClusterIP를 가지고 있다. Service를 Pod에 연결을 시켜놓으면, Service IP를 가지고..
somaz.tistory.com
N/W
서버 유형 | Hostname | OS | CPU | 메모리 | 서비스망 IP | 내부망 IP | 계정 |
가상 머신 | dh-k8s-master | CentOS 7 | 2 Cores | 4G | 192.168.21.112 (/24) | 10.1.1.112 (/8) | root, somaz |
가상 머신 | dh-k8s-node | CentOS 7 | 2 Cores | 4G | 192.168.21.113 (/24) | 10.1.1.113 (/8) | root, somaz |
가상 머신 | dh-k8s-node2 | CentOS 7 | 2 Cores | 4G | 192.168.21.114 (/24) | 10.1.1.113 (/8) | root, somaz |
1. ClusterIP
실습 1) Pod 생성 후 ClusterIP 로 Service 생성
pod를 하나 생성한다.
$ vi pod-1.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-1
labels:
app: pod
spec:
nodeSelector:
kubernetes.io/hostname: dh-k8s-node
containers:
- name: container
image: tmkube/app
ports:
- containerPort: 8080
$ kubectl apply -f pod-1.yaml
pod/pod-1 created
service를 생성한다.
apiVersion: v1
kind: Service
metadata:
name: svc-1
spec:
selector:
app: pod
ports:
- port: 9000
targetPort: 8080
type: ClusterIP # type의 default 값이 ClusterIP이다. 생략 가능하다.
service apply 해보자.
$ kubectl apply -f svc-1.yaml
error: error validating "svc-1.yaml": error validating data: ValidationError(Service): unknown field "type" in io.k8s.api.core.v1.Service; if you choose to ignore these errors, turn validation off with --validate=false
# 에러가 발생한다. type:ClusterIP를 지우고 apply 해보자.
$ kubectl apply -f svc-1.yaml
service/svc-1 created
$ kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 45h <none>
svc-1 ClusterIP 10.108.27.243 <none> 9000/TCP 14s app=pod
$ curl 10.108.27.243:9000/hostname
Hostname : pod-1
그러나 web에서는 열리지 않는다. 왜냐하면 ClusterIP로 설정되어 있기 때문이다.
이해가 가지 않는다면 아래의 포스팅을 참고하기 바란다.
2022.08.29 - [교육, 커뮤니티 후기] - <인프런> 대세는 쿠버네티스 [초급] - No.9 Object - Service
실습 2) Pod 삭제 후 재생성
# pod 확인
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-1 1/1 Running 0 4m27s 172.16.12.146 dh-k8s-node <none> <none>
# pod 삭제 후 재생성
$ kubectl delete -f pod-1.yaml
pod "pod-1" deleted
$ kubectl apply -f pod-1.yaml
pod/pod-1 created
# pod 다시 확인
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-1 1/1 Running 0 32s 172.16.12.147 dh-k8s-node <none> <none>
# service 확인
$ kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 46h <none>
svc-1 ClusterIP 10.108.27.243 <none> 9000/TCP 31m app=pod
$ curl 10.108.27.243:9000/hostname
Hostname : pod-1
2. NodePort
실습 1) svc-1 삭제 후 NodePort 로 svc-2 생성
$ kubectl delete -f svc-1.yaml
service "svc-1" deleted
apiVersion: v1
kind: Service
metadata:
name: svc-2
spec:
selector:
app: pod
ports:
- port: 9000
targetPort: 8080
nodePort: 30000
type: NodePort
$ kubectl apply -f svc-2.yaml
service/svc-2 created
$ kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 46h <none>
svc-2 NodePort 10.101.105.144 <none> 9000:30000/TCP 4s app=pod
# 9000번은 ClusterIP로 접근했을 때 사용하는 포트이다.
# 30000번은 NodePort로 접근했을 때 사용하는 포트이다.
먼저 dh-k8s-node는 192.168.21.113 (/24) 사용하고, dh-k8s-node2는 192.168.21.114 (/24) 를 사용한다.
나는 사내 서버를 이용해 TestServer를 구성했다. 사내 서버에서 접근을 해보자.
$ curl 192.168.21.113:30000/hostname
Hostname : pod-1
$ curl 192.168.21.114:30000/hostname
Hostname : pod-1
실습 2) pod-2 생성 후 Traffic 분산을 확인
$ vi pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-2
labels:
app: pod
spec:
nodeSelector:
kubernetes.io/hostname: dh-k8s-node2
containers:
- name: container
image: tmkube/app
ports:
- containerPort: 8080
$ kubectl apply -f pod-2.yaml
pod/pod-2 created
이제 사내서버에서 Traffic 분산을 확인해보자.
$ curl 192.168.21.113:30000/hostname
Hostname : pod-2
$ curl 192.168.21.113:30000/hostname
Hostname : pod-2
$ curl 192.168.21.113:30000/hostname
Hostname : pod-1
$ curl 192.168.21.113:30000/hostname
Hostname : pod-1
$ curl 192.168.21.114:30000/hostname
Hostname : pod-2
$ curl 192.168.21.114:30000/hostname
Hostname : pod-2
$ curl 192.168.21.114:30000/hostname
Hostname : pod-1
$ curl 192.168.21.114:30000/hostname
Hostname : pod-1
- Traffic 분산이 잘 되고 있다.
실습 3) svc-2 삭제 후 `externalTrafficPolicy: Local` 옵션을 써서 svc-2 생성
$ kubectl delete -f svc-2.yaml
service "svc-2" deleted
$ vi svc-2.yaml
apiVersion: v1
kind: Service
metadata:
name: svc-2
spec:
selector:
app: pod
ports:
- port: 9000
targetPort: 8080
nodePort: 30000
type: NodePort
externalTrafficPolicy: Local
$ kubectl apply -f svc-2.yaml
service/svc-2 created
이제 확인해보자.
$ curl 192.168.21.113:30000/hostname
Hostname : pod-1
$ curl 192.168.21.113:30000/hostname
Hostname : pod-1
$ curl 192.168.21.113:30000/hostname
Hostname : pod-1
$ curl 192.168.21.113:30000/hostname
Hostname : pod-1
$ curl 192.168.21.114:30000/hostname
Hostname : pod-2
$ curl 192.168.21.114:30000/hostname
Hostname : pod-2
$ curl 192.168.21.114:30000/hostname
Hostname : pod-2
$ curl 192.168.21.114:30000/hostname
Hostname : pod-2
- `externalTrafficPolicy` 옵션을 써주었기 때문에 이제 해당 node IP로는 해당 node의 pod만 불러오는 것이다.
3. Load Balancer
실습 1) type: LoadBalancer로 svc-3 생성
$ vi svc-3.yaml
apiVersion: v1
kind: Service
metadata:
name: svc-3
spec:
selector:
app: pod
ports:
- port: 9000
targetPort: 8080
type: LoadBalancer
$ kubectl apply -f svc-3.yaml
service/svc-3 created
$ kubectl get svc svc-3
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-3 LoadBalancer 10.104.60.113 <pending> 9000:31735/TCP 65s
# pending 상태인 이유는 LoadBalancer 플러그인이 설치가 안되어 있기 때문이다.
4. 마무리
`Sample.yaml`
apiVersion: v1
kind: Service
metadata:
name: svc-3
spec:
selector: # Pod의 Label과 매칭
app: pod
ports:
- port: 9000 # Service 자체 Port
targetPort: 8080 # Pod의 Container Port
type: ClusterIP, NodePort, LoadBalancer # 생략시 ClusterIP
externalTrafficPolicy: Local, Cluster # 트래픽 분배 역할
NodePort
- Node Port의 범위 : 30000~32767
마무리
이번 실습을 통해 Kubernetes 서비스의 구조와 동작 원리를 훨씬 더 명확하게 이해할 수 있었다.
- ClusterIP: 기본 타입으로, 내부 통신 전용이며 외부에서는 접근 불가.
- NodePort: 외부에서 접근 가능, 클러스터의 모든 노드 IP + 노출 포트(30000~32767)를 통해 접속.
- externalTrafficPolicy: Local: 클라이언트 IP 보존 가능, 해당 노드의 Pod만 트래픽을 받음.
- LoadBalancer: 클라우드 환경에서 외부 IP를 자동 할당받아 사용하지만, 메탈 환경에서는 별도 구성 필요.
이처럼 Kubernetes의 서비스 오브젝트는 단순한 네트워크 연결 수단이 아니라, 트래픽 분산, 접근 제어,
확장성 및 안정성을 보장하는 핵심 요소이다. 특히 실무에서 서비스 노출 방식 선택은 아키텍처 설계에 큰 영향을 미치므로
이러한 차이점을 정확히 이해하는 것이 중요하다.
실습을 통해 이론을 체화하니, 쿠버네티스 네트워크의 퍼즐이 조금씩 맞춰지는 느낌이다.
다음엔 Ingress와의 연계를 통해 더 풍부한 시나리오도 다뤄보자!
Referenece
Kubernetes
Others
- Kubernetes NodePort vs LoadBalancer vs Ingress? : https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0
'교육, 커뮤니티 후기 > 인프런 교육' 카테고리의 다른 글
<인프런> 대세는 쿠버네티스 [초급] - No.8 Object - Pod 실습 (0) | 2022.08.30 |
---|---|
<인프런> 대세는 쿠버네티스 [초급] - No.9 Object - Service (0) | 2022.08.29 |
<인프런> 대세는 쿠버네티스 [초급] - No.7 Object - Pod (0) | 2022.08.29 |
<인프런> 대세는 쿠버네티스 [초급] - No.6 Kubernetes Cluster 설치(v1.22) (0) | 2022.08.29 |
<인프런> 대세는 쿠버네티스 [초급] - No.5 Kubernetes Overview (0) | 2022.08.29 |