Kubernetes의 Service는 클러스터 내에서 실행 중인 Pod 간의 네트워크 통신을 안정적으로 관리하고, 외부 클라이언트가 파드에 접근할 수 있도록 하는 추상화된 네트워크 리소스이다.
Service는 파드의 동적인 IP 주소 문제를 해결하고, 여러 Pod에 트래픽을 분산(로드 밸런싱)시키는 역할을 한다.
Service의 주요 역할
- 파드 간 통신 안정화: Service는 고정된 이름과 IP 주소를 제공하여 파드의 네트워크 위치 변화를 추상화한다.
- 파드의 IP 주소는 재시작 시 변경될 수 있다.
- 로드 밸런싱: 여러 복제본(replica) 파드에 트래픽을 균등하게 분산
- 외부 트래픽 연결: 외부 클라이언트(예: 브라우저)에서 클러스터 내 파드에 접근할 수 있는 네트워크 엔드포인트를 제공합니다.
- DNS 통합: 쿠버네티스 클러스터에서 Service는 DNS 이름으로 등록되어 클러스터 내부에서 쉽게 접근할 수 있습니다.
Service의 동작 원리
- Selector로 파드 선택: Service는 Selector에 지정된 라벨을 가진 파드를 감지.
- Service는 특정 Selector 없이도 동작할 수 있지만, 이 경우 수동으로 Endpoints를 설정해야 한다. - Endpoints 생성: Service는 연결된 파드의 IP 주소와 포트를 Endpoints 리소스에 저장.
- 트래픽 라우팅: Service는 로드 밸런싱을 통해 트래픽을 적절한 파드로 전달.
- DNS를 통한 접근: 클러스터 내부에서는 Service 이름을 사용해 접근 가능.
서비스 = 쿠버네티스 네트워크
즉, Pod를 묶어주는 Virtual IP
[master ~/controller]$cat > nodeport-nginx.yaml apiVersion: v1 kind: Service metadata: name: nodeport-service spec: type: NodePort clusterIP: 10.8.0.200 selector: app: webui ports: - protocol: TCP port: 8080 targetPort: 80 nodePort: 30200 [master ~/controller]$kubectl create -f nodeport-nginx.yaml service/nodeport-service created [master ~/controller]$kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.8.0.1 <none> 443/TCP 4m43s nodeport-service NodePort 10.8.0.200 <none> 8080:30200/TCP 10s # 워커노드에서 확인 [root@node1 ~]$netstat -anpt | grep 30200
# Kubernetes Service란
- 동일한 서비스를 제공하는 Pod 그룹의 단일 진입점을 제공
- Service는 Selector를 사용하여 특정 라벨(Label)을 가진 파드와 연결된다.
- Selector: app: webui 라벨을 가진 파드와 연결
- Port: 클라이언트가 접근할 Service의 포트(80)
- TargetPort: 파드 내부 컨테이너의 포트(80)
- clusterIP는 생략 가능= clusterIP(=LB IP, virtual IP)로 접속하면 App로 접속하게 된다.
Kubernetes Service Type
- ClusterIP(default)
- 클러스터 내부에서만 접근 가능한 가상 IP 제공 - pod 그룹의 단일 진입점(Virtual IP) 생성
- 즉, Selector의 label가 동일한 파드들의 그룹으로 묶어 단일 진입점 생성 (=service API)
- 클러스터 내부에서만 사용 가능
- ex) 백엔드 파드가 프런트엔드 파드와 통신할 때
- type 생략 시 default값으로 10.96.0.0/12 범위에서 랜덤 할당
- 고정시킬 수는 있으나, IP 충돌을 예방하기 위해 보통 고정하지 않는다.
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
selector:
app: backend # backend 라벨 가진 파드와 연결
ports:
- protocol: TCP
port: 8080 # 클러스터 내부에서 8080 포트로 접근하면
targetPort: 80 # 파드의 80 포트로 트래픽 전달한다.
- Node Port
- Cluster IP가 생성된 후, 모든 Worker Noder에 외부에서 접속 가능한 포트가 예약
- 클러스터 외부에서 'Cluster IP:Node Port'로 접근
- 각 노드의 고정 포트를 열어 모든 노드를 대상으로 외부에서 접근할 수 있도록 설정
- 클러스터 외부에서 접근 가능하므로 보안에 주의해야 한다.
- Default Nodeport 범위: 30000-32767
- ClusterIP 생성 후 Nodeport 예약
- 디버깅이나 간단한 테스트용으로 사용
apiVersion: v1
kind: Service
metadata:
name: nodeport-service
spec:
type: NodePort
selector:
app: my-app
ports:
- protocol: TCP
port: 8080
targetPort: 80
nodePort: 30007 # 클러스터 외부에서 NodeIP:30007로 접근 가능
# 노드의 모든 IP 주소에서 접근 가능
- LoadBalancer
- Public Cloud: 클라우드 인프라스트럭처(AWS, Azure, GCP 등)나 오픈스택 클라우드 적용
- LoadBalancer를 자동으로 Provision하는 기능 지원
- NodePort를 예약 후 해당 nodeport로 외부 접근 허용
# 외부에서 로드 밸런서의 IP 주소를 통해 접근 가능
apiVersion: v1
kind: Service
metadata:
name: loadbalancer-service # 클라우드 제공자의 로드 밸런서 생성
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 80
- External Name
- 클러스터 안에서 외부에 접속 시 사용할 도메인을 등록해서 사용 (DNS 기능 지원)
- 클러스터 외부의 특정 DNS 이름을 Service로 매핑하여 클러스터 내부에서 외부로 나갈 수 있게 도와준다.
- ex) 외부 데이터베이스와 통합
kubectl expose
- kubectl expose 명령어는 Kubernetes 리소스를 기반으로 Service를 생성하는 명령어
- 리소스의 네트워크 노출: kubectl expose는 Pod, Deployment, ReplicaSet, 또는 ReplicationController와 같은 리소스에 접근할 수 있는 Service를 생성한다.
# Deployment 기반의 Service 생성
$ kubectl expose deployment my-deployment --port=80 --target-port=8080 --type=ClusterIP
# --port Service의 포트를 지정. (클라이언트가 접근하는 포트)
# --target-port Pod 또는 컨테이너에서 사용하는 포트를 지정.
# 외부에 노출
$ kubectl expose pod my-pod --port=80 --target-port=8080 --type=NodePort
# Service는 자동으로 NodePort 범위(30000~32767) 내 포트를 할당.
# 사용자 정의 라벨 Selector
$ kubectl expose pod my-pod --port=80 --selector="app=my-app,env=prod"
# ClusterIP 실습
[master ~]$ kubectl run http --image=httpd:alpine --port=80 --expose=true
service/http created
pod/http created
[master ~]$ kubectl describe svc http
Name: http
Namespace: default
Labels: <none>
Annotations: <none>
Selector: run=http
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.43.114.52
IPs: 10.43.114.52
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.42.0.19:80
Session Affinity: None
Internal Traffic Policy: Cluster
Events: <none>
[master ~]$cat > deploy-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webui
spec:
replicas: 3
selector:
matchLabels:
app: webui
template:
metadata:
name: nginx-pod
labels:
app: webui
spec:
containers:
- name: nginx-container
image: nginx:1.14
[master ~]$kubectl create -f deploy-nginx.yaml
deployment.apps/webui created
[master ~]$cat > clusterip-nginx.yaml
apiVersion: v1
kind: Service
metadata:
name: clusterip-service
spec:
type: ClusterIP
clusterIP: 10.8.0.100
selector:
app: webui # webui pod를 묶어서 단일 진입점으로 관리하겠다.
ports:
- protocol: TCP
port: 80 # 10.100.100.100의 포트
targetPort: 80 # Pod의 포트
[master ~]$kubectl create -f clusterip-nginx.yaml
service/clusterip-service created
[master ~]$kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
clusterip-service ClusterIP 10.8.0.100 <none> 80/TCP 22s # 확인
kubernetes ClusterIP 10.8.0.1 <none> 443/TCP 6d8h
[master ~]$kubectl describe svc clusterip-service
Name: clusterip-service
Namespace: default
Labels: <none>
Annotations: cloud.google.com/neg: {"ingress":true}
Selector: app=webui
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.8.0.100
IPs: 10.8.0.100
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.4.0.14:80,10.4.0.15:80,10.4.2.5:80 # 여기 3개중에 하나랑 균등하게 분산(LB)되어 실행된다
Session Affinity: None
Events: <none>
# scale up시 Endpoints 갯수가 늘어나느 것 확인
[master ~]$kubectl scale deployment webui --replicas=5
deployment.apps/webui scaled
[master ~]$kubectl describe svc clusterip-service
Name: clusterip-service
Namespace: default
Labels: <none>
Annotations: cloud.google.com/neg: {"ingress":true}
Selector: app=webui
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.8.0.100
IPs: 10.8.0.100
Port: <unset> 8080/TCP
TargetPort: 80/TCP
Endpoints: 10.4.0.14:80,10.4.0.15:80,10.4.1.14:80 + 2 more...
Session Affinity: None
Events: <none>
# LoadBalance 실습
[master ~/controller]$cat > loadbalancer-nginx.yaml
apiVersion: v1
kind: Service
metadata:
name: loadbalancer-service
spec:
type: LoadBalancer
selector:
app: webui
ports:
- protocol: TCP
port: 80
targetPort: 80
[master ~/controller]$kubectl create -f loadbalancer-nginx.yaml
service/loadbalancer-service created
# 로드밸런스를 만들면 EXTERNAL-IP에 LB의 IP ADDR(+방화벽)이 들어온다.
[master ~/controller]$kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.8.0.1 <none> 443/TCP 6h30m
loadbalancer-service LoadBalancer 10.8.11.120 35.225.72.184 80:30924/TCP 43s
nodeport-service NodePort 10.8.0.15 <none> 80:30200/TCP 84s
# External Name 실습
[master ~]$cat external-name.yaml
apiVersion: v1
kind: Service
metadata:
name: externalname-svc
spec:
type: ExternalName
externalName: google.com
[master ~]$ubectl create -f external-name.yaml
service/externalname-svc created
[master ~]$kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
externalname-svc ExternalName <none> google.com <none> 12s
kubernetes ClusterIP 10.8.0.1 <none> 443/TCP 2m45s
[master ~]$kubectl run testpod -it --image=centos:7
If you don't see a command prompt, try pressing enter.
[root@testpod /]curl externalname-svc.default.svc.cluster.local
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
<title>Error 404 (Not Found)!!1</title>
<style>
*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
</style>
<a href=//www.google.com/><span id=logo aria-label=Google></span></a>
<p><b>404.</b> <ins>That’s an error.</ins>
<p>The requested URL <code>/</code> was not found on this server. <ins>That’s all we know.</ins>
# google로 접속
# Headless Service
- ClusterIP가 없는 서비스로 단일 진입점이 필요 없을 때 사용
- Service와 연결된 Pod의 endpoint로 DNS 레코드가 생성됨 :: DNS resolving service로 요청 가능
- 즉, Pod들의 endpoint에 DNS resolving service 지원
- Pod의 DNS 주소: Pod-id-addr.namespace.pod.cluster.local
[master ~]$cat > headless-nginx.yaml
apiVersion: v1
kind: Service
metadata:
name: headless-service # 이게 설정되어 있으면 Headless service임, 따로 명시하진 않음
spec:
type: ClusterIP # 단일진입점으로 ClusterIP는 만들되, IP는 없어도 된다.
clusterIP: None
selector:
app: webui
ports:
- protocol: TCP
port: 80
targetPort: 80
[master ~]$kubectl create -f deploy-nginx.yaml
deployment.apps/webui created
[master ~]$kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
webui-9cc457697-gbbrz 1/1 Running 0 8s 10.4.0.18 gke-cluster-1-default-pool-697aeef7-5nx7 <none> <none>
webui-9cc457697-hkmrv 1/1 Running 0 8s 10.4.2.7 gke-cluster-1-default-pool-697aeef7-ccmj <none> <none>
webui-9cc457697-jgpm4 1/1 Running 0 8s 10.4.0.19 gke-cluster-1-default-pool-697aeef7-5nx7 <none> <none>
# clusterIP와 비교해볼 것임
[master ~]$kubectl create -f clusterip-nginx.yaml
service/clusterip-service created
[master ~]$kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
clusterip-service ClusterIP 10.8.0.100 <none> 8080/TCP 6s
kubernetes ClusterIP 10.8.0.1 <none> 443/TCP 39m
[master ~]$kubectl create -f headless-nginx.yaml
service/headless-service created
[master ~]$kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
clusterip-service ClusterIP 10.8.0.100 <none> 8080/TCP 43s
headless-service ClusterIP None <none> 80/TCP 11s # Cluster-IP None
kubernetes ClusterIP 10.8.0.1 <none> 443/TCP 40m
[master ~]$kubectl describe svc headless-service
Name: headless-service
Namespace: default
Labels: <none>
Annotations: cloud.google.com/neg: {"ingress":true}
Selector: app=webui
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: None #Cluster-IP
IPs: None
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.4.0.18:80,10.4.0.19:80,10.4.2.7:80
Session Affinity: None
Events: <none>
[master ~]$kubectl run testpod --image=centos:7 -it /bin/bash
If you don't see a command prompt, try pressing enter.
[root@testpod /]# cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local c.caramel-element-415606.internal google.internal
nameserver 10.8.0.10 # core-dns의 IP
options ndots:5
# 10.4.2.7 Pod와 연결시켜주는 resolving service 확인
[root@testpod /]# curl 10-4-2-7.default.pod.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
# kube-proxy
- kubernetes service의 Backend 구현
- endpoint 연결을 위한 iptables 구성
- nodePort로의 접근과 Pod 연결을 구현(iptables rule 구성)
[master ~]$kubectl get pods -n kube-system | grep proxy
kube-proxy-gke-cluster-1-default-pool-697aeef7-5nx7 1/1 Running 0 8d
kube-proxy-gke-cluster-1-default-pool-697aeef7-7ss7 1/1 Running 0 8d
kube-proxy-gke-cluster-1-default-pool-697aeef7-ccmj 1/1 Running 0 8d
# 노드에 접속해서 확인해보면 iptables rule을 확인할 수 있다.
[root@node1 ~]$iptables -t nat -S | grep 80
-A KUBE-SEP-COZRZBQNU5VVKBBP -p tcp -m comment --comment "default/clusterip-service" -m tcp -j DNAT --to-destination 10.4.0.18:80
-A KUBE-SEP-DUNFEYM2OH36MSQP -p tcp -m comment --comment "kube-system/default-http-backend:http" -m tcp -j DNAT --to-destination 10.4.0.8:8080
-A KUBE-SEP-PPZDR4DAUPEHW5MR -p tcp -m comment --comment "default/clusterip-service" -m tcp -j DNAT --to-destination 10.4.2.7:80
-A KUBE-SEP-TRPQWLCKYQVY3LNP -p tcp -m comment --comment "default/clusterip-service" -m tcp -j DNAT --to-destination 10.4.0.19:80
-A KUBE-SVC-KUSV3H3OUX7H3MDQ -m comment --comment "default/clusterip-service -> 10.4.0.18:80" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-COZRZBQNU5VVKBBP
-A KUBE-SVC-KUSV3H3OUX7H3MDQ -m comment --comment "default/clusterip-service -> 10.4.0.19:80" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-TRPQWLCKYQVY3LNP
-A KUBE-SVC-KUSV3H3OUX7H3MDQ -m comment --comment "default/clusterip-service -> 10.4.2.7:80" -j KUBE-SEP-PPZDR4DAUPEHW5MR
-A KUBE-SVC-XP4WJ6VSLGWALMW5 -m comment --comment "kube-system/default-http-backend:http -> 10.4.0.8:8080" -j KUBE-SEP-DUNFEYM2OH36MSQP
⭐kube-proxy mode
- userspace (K8S 초창기에 사용)
- 클라이언트의 서비스 요청을 iptables를 거쳐 kube-proxy가 받아서 연결
- ⭐iptables : default k8s network mode
- kube-proxy는 service API 요청 시 iptables rule이 생성
- 클라이언트 연결은 kube-proxy가 받아서 (LISTEN 통신) iptables 룰을 통해 연결
- IPVS : 리눅스 커널이 지원하는 L4 로드 밸런싱 기술 이용
- 별도의 ipvs 지원 모듈을 설정한 후 적용 가능
Service와 Ingress의 차이
역할 | 파드와 연결된 네트워크 엔드포인트를 제공 | HTTP/HTTPS 요청을 라우팅 |
기능 | TCP/UDP 트래픽 전달 | 도메인 및 경로 기반 트래픽 라우팅 |
사용 사례 | 파드에 직접 연결하거나 로드 밸런싱 제공 | 여러 서비스에 트래픽을 라우팅해야 하는 경우 |
반응형
'Container > Kubernetes' 카테고리의 다른 글
[K8S] 쿠버네티스 레이블 (0) | 2024.03.17 |
---|---|
[K8S] Ingress Controller (0) | 2024.03.07 |
[K8S] Cronjob Controller (0) | 2024.02.28 |
[K8S] Job Controller (0) | 2024.02.27 |
[K8S] Statefulset (0) | 2024.02.26 |