✔️ Service & DNS
파드 집합에서 실행중인 애플리케이션을 네트워크 서비스로 노출하는 추상화 방법
쿠버네티스는 파드에게 고유한 IP 주소와 파드 집합에 대한 단일 DNS 명을 부여하고, 그것들 간에 로드-밸런스를 수행할 수 있다.
RS(ReplicaSet)을 만들고 복제본 개수를 3으로 하면 파드 3개가 만들어지고 파드마다 고유한 ip가 부여된다.
컨테이너가 여러개 있더라도 ip는 파드에 하나만 부여된다. 컨테이너의 개수와 상관없이 같은 ip를 갖는다.
컨테이너에서 web app을 실행하고 있다면 클라이언트 입장에서 누구를 어떻게 찾아가야 하는지 알 수 없다.
그래서 우리는 Service
라는 리소스를 만들게 되며 Service
리소스는 각 파드와 연결된다.
SVC(Service)에는 Selector가 있고 파드는 Label을 갖는다. 레이블과 셀렉터의 관계로 셀렉팅을 하게 된다.
SVC는 DNS 이름이 부여가 된다. DNS 이름이 부여되기 위해서는 kube-dns라는 add-on이 구성되어 있어야 하지만 kubeadm에서 알아서 설치된다.
kube-dns
을 구현하기 위해 coredns를 사용한다. kube-dns
는 클러스터 내부에서 사용한다.
클러스터 외부에서 사용하기 위해서는 따로 DNS 서버(리눅스의 Bind, AWS의 Route 53)를 구축해야 한다.
SVC 리소스를 만들면 kube-dns에 의해 서비스에 이름이 부여된다.
클라이언트는 해당 이름을 가지고 서비스에 접근하면 서비스는 로드 밸런서의 기능을 가지고 있으므로 트래픽을 분산해서 접근이 가능하도록 해준다.
서비스를 만들어보자
vagrant@node1 ~/svc kubectl api-resources | grep services
services svc v1 true Service
apiservices apiregistration.k8s.io/v1 false APIService
SVC
라는 shortname을 가지며 core 그룹의 v1, namspace 및 Service
라는 kind를 사용한다.
vagrant@node1 ~/svc kubectl explain svc.spec
clusterIP
- 서비스의 ip를 직접 할당할 수는 있으나 사용하지 않는다.
kube-dns 가 있기 이전에는 이름이라는 것이 없어서 서비스의 ip를 고정시켜주는 것이 편리했다.
ip를 고정해서 설정하는 방식을 사용하면 파드나 서비스가 몇개 없을 때는 사용하고 있지 않은 ip를 찾는 것이 어렵지 않지만
점차 서비스가 증가하면 사용하고 있지 않은 ip를 골라 사용하는 것이 어려우며 ip를 고르는 도중 다른 누군가가 해당 ip를 가로채면 또 문제가 생긴다.
그래서 ip를 부여할 수는 있지만 굳이 쓸 필요가 없다.
우리는 dns에서 이름을 부여하기 때문에 ip가 몇번이될지 알 필요가 없다.
서비스 리소스에 이름을 주면 그 이름이 주소에 반영이 되므로 예측 가능하다.
ports
- 서비스의 port
로드 밸런서가 있고 VM이 있을 때 로드 밸런서에 접속할 포트가 있고 로드 밸런서에 백엔드에 연결되어 있는 VM에 접속하는 포트들도 있다.selector
- 서비스가 연결하기 위한 파드의 레이블type
-ExternalName
,ClusterIP(default)
,NodePort
, andLoadBalancer
NodePort
,LoadBalancer
→ 클러스터 외부에 노출하기 위한 것ClusterIP
- 클러스터 내부
vagrant@node1 ~/svc kubectl explain svc.spec.ports
KIND: Service
VERSION: v1
RESOURCE: ports <[]Object>
리스트로 되어 있으며 포트에 이름을 부여할 수 있다.
port
- 실제 서비스에 노출시킬 포트를 지정하는 부분protocol
- 접속하는 프로토콜 TCP(default), UDP, SCTPtargetPort
- 파드의 포트
myweb-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: myweb-svc
spec:
selector:
app: web
ports:
- port: 80
targetPort: 8080
myweb-rs.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myweb-rs
spec:
replicas: 3
selector:
matchLabels:
app: web
env: dev
template:
metadata:
labels:
app: web
env: dev
spec:
containers:
- name: myweb
image: ghcr.io/c1t1d0s7/go-myweb
ports:
- containerPort: 8080
protocol: TCP
두개의 yaml 로 생성하기 위해서 다음 명령을 사용한다.
vagrant@node1 ~/svc kubectl create -f myweb-rs.yaml myweb-svc.yaml
vagrant@node1 ~/svc kubectl create -f .
첫번째처럼 두개의 파일을 직접 지정해서 실행하거나
두번째처럼 디렉토리 내의 모든 파일을 한번에 실행할 수 있다.
vagrant@node1 ~ kubectl get rs,po,svc
NAME DESIRED CURRENT READY AGE
replicaset.apps/myweb-rs 3 3 3 17s
NAME READY STATUS RESTARTS AGE
pod/myweb-rs-f2q75 1/1 Running 0 17s
pod/myweb-rs-fddnd 1/1 Running 0 17s
pod/myweb-rs-qz54p 1/1 Running 0 17s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.233.0.1 <none> 443/TCP 3d7h
service/myweb-svc ClusterIP 10.233.36.19 <none> 80/TCP 17s
ClusterIP
- 클러스터 내부에서만 사용 가능한 로드 밸런서10.233.36.19
- 클러스터의 ip, 랜덤하게 부여됨<none>
- 외부용이 아니므로 EXTERNAL-IP는 없음80/TCP
- 서비스에 접근하기 위해 해당 포트로 접근할 수 있다.
73 kube_service_addresses: 10.233.0.0/18
74
75 # internal network. When used, it will assign IP
76 # addresses from this range to individual pods.
77 # This network must be unused in your network infrastructure!
78 kube_pods_subnet: 10.233.64.0/18
kube_service_addresses
- SVC는 해당 대역을 사용kube_pods_subnet
- 파드는 해당 대역을 사용
앞서 ClusterIP 에 할당된 10.233.36.19
은 kube_service_addresses
를 사용한다.
비교적 큰 네트워크(/18 - 16,384개)가 잡혀있다.
88 # - kubelet_max_pods: 110
kubelet 하나당 110개의 파드로 제한이 걸린다. 한 노드당 110개를 넘어갈 수 없다.
노드가 3개라면 총 330개가 최대치이다. 쿠버네티스에서는 이 개수를 시스템이 안정적인 최대 개수라고 생각한다.
vagrant@node1 ~ kubectl get svc myweb-svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myweb-svc ClusterIP 10.233.36.19 <none> 80/TCP 10m
vagrant@node1 ~ kubectl describe svc myweb-svc
Name: myweb-svc
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=web
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.233.36.19
IPs: 10.233.36.19
Port: <unset> 80/TCP
TargetPort: 8080/TCP
Endpoints: 10.233.90.27:8080,10.233.92.71:8080,10.233.96.50:8080
Session Affinity: None
Events: <none>
vagrant@node1 ~ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myweb-rs-f2q75 1/1 Running 0 11m 10.233.96.50 node2 <none> <none>
myweb-rs-fddnd 1/1 Running 0 11m 10.233.90.27 node1 <none> <none>
myweb-rs-qz54p 1/1 Running 0 11m 10.233.92.71 node3 <none> <none>
Endpoints
의 ip와 리소스의 ip가 일치하며 Endpoints
에 타겟 포트인 8080이 붙는다.
myweb-svc가 있고 client가 접속하는 포트 번호는 80번이고 myweb-svc의 ip는 10.233.36.19 이다.
클라이언트는 해당 ip와 포트로 들어올 수 있다.
ep는 백엔드인데 파드는 셀렉터에의해 셀렉팅되고 셀렉팅 된 파드가 엔드포인트에 들어오게 된다.
테스트를 위해 임시로 실행해보자
vagrant@node1 ~ kubectl run client -it --image ubuntu bash
If you don't see a command prompt, try pressing enter.
root@client:/# apt update
이 서비스는 외부용이 아니고 내부에서 파드와 파드끼리 통신할 때 사용한다.
curl 명령어를 설치한 뒤 서비스의 ip로 접속해보면
root@client:/# curl 10.233.36.19
Hello World!
myweb-rs-fddnd
root@client:/# curl 10.233.36.19
Hello World!
myweb-rs-qz54p
root@client:/# curl 10.233.36.19
Hello World!
myweb-rs-f2q75
root@client:/# curl 10.233.36.19
Hello World!
myweb-rs-fddnd
정상적으로 접속이 되며 로드밸런싱이 되는 것을 확인할 수 있다.
클라이언트라는 파드를 만들었고 서비스의 포트는 80이며 ip는 10.233.36.19이다.
RS에 의해 만들어진 3개의 파드는 SVC에 연결되어 있고 파드의 모든 포트는 8080이다.
client에서 SVC로 접속을 하면 SVC는 자신이 가진 ep 중 하나로 연결을 시켜준다.
vagrant@node1 ~ kubectl describe svc myweb-svc
Name: myweb-svc
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=web
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.233.36.19
IPs: 10.233.36.19
Port: <unset> 80/TCP
TargetPort: 8080/TCP
Endpoints: 10.233.90.27:8080,10.233.92.71:8080,10.233.96.50:8080
Session Affinity: None
Events: <none>
vagrant@node1 ~ kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.100.100:6443 3d8h
myweb-svc 10.233.90.27:8080,10.233.92.71:8080,10.233.96.50:8080 24m
SVC의 Endpoints
는 endpoints의 myweb-svc 의 주소이다.
서비스를 만들면 서비스의 이름과 똑같은 이름의 endpoint가 만들어진다.
우리가 서비스라고 하는 오브젝트의 리소스를 만들었고 파드가 있을 때
SVC의 셀렉터가 파드들을 셀렉팅할 것이고 셀렉팅이 된 정보를 ep가 가지고 있다.
endpoint의 목록을 가진 것은 서비스가 아니라 서비스와 같은 이름을 가지는 엔드포인트라고 하는 오브젝트 리소스가 가지고 있다.
vagrant@node1 ~ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
client 1/1 Running 1 (2m57s ago) 10m 10.233.92.72 node3 <none> <none>
myweb-rs-f2q75 1/1 Running 0 27m 10.233.96.50 node2 <none> <none>
myweb-rs-fddnd 1/1 Running 0 27m 10.233.90.27 node1 <none> <none>
myweb-rs-qz54p 1/1 Running 0 27m 10.233.92.71 node3 <none> <none>
vagrant@node1 ~ kubectl delete po myweb-rs-qz54p
pod "myweb-rs-qz54p" deleted
가장 하단의 myweb-rs-qz54p
를 지우고 다시 확인해보면
vagrant@node1 ~ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
client 1/1 Running 1 (3m54s ago) 11m 10.233.92.72 node3 <none> <none>
myweb-rs-f2q75 1/1 Running 0 28m 10.233.96.50 node2 <none> <none>
myweb-rs-fddnd 1/1 Running 0 28m 10.233.90.27 node1 <none> <none>
myweb-rs-rrxr5 1/1 Running 0 36s 10.233.92.73 node3 <none> <none>
vagrant@node1 ~ kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.100.100:6443 3d8h
myweb-svc 10.233.90.27:8080,10.233.92.73:8080,10.233.96.50:8080 28m
myweb-rs-qz54p
의 ip인 10.233.92.71
를 지웠기 때문에 ep의 my-svc의 ip 목록에서 10.233.92.71
가 삭제되고 새롭게 생성된 myweb-rs-rrxr5
의 ip인 10.233.92.73
이 추가된 것을 확인할 수 있다.
실제로 레이블 셀렉팅에 해당되는 파드의 정보는 엔드포인트가 가지고 있음을 알 수 있다.
따라서 백엔드가 바뀌면 알아서 엔드포인트가 그것을 감지해서 업데이트한다.
엔드포인트는 서비스를 만들면 서비스의 이름과 똑같은 이름을 갖는 엔드포인트가 만들어지며 엔드포인트가 셀렉팅을 한 해당되는 파드의 정보를 가지고 있다.
static 파드를 이용해서 테스트용 파드를 하나 띄워놓자
vagrant@node1 ~ kubectl run nettool -it --image ghcr.io/c1t1d0s7/network-multitool
If you don't see a command prompt, try pressing enter.
/ # curl 10.233.36.19
Hello World!
myweb-rs-f2q75
접속이 잘 된다.
/ # host www.google.com
www.google.com has address 142.251.42.196
www.google.com has IPv6 address 2404:6800:4004:81f::2004
/ #
host
명령어로 DNS 서버에 질의한다.
/ # cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local kornet
nameserver 169.254.25.10
options ndots:5
리눅스의 DNS 서버인 169.254.25.10
에 물어본다.
결론적으로는 169.254.25.10
가 coredns 서버이다.
/ # host myweb-svc
myweb-svc.default.svc.cluster.local has address 10.233.36.19
서비스의 이름이 부여되는 것을 확인할 수 있다.
ip 주소대신 서비스 이름으로 질의하면 ip를 리턴해준다.
따라서 ip를 알 필요가 없다. 서비스의 이름에 질의하면 ip를 알아서 응답해주기 때문이다.
/ # curl myweb-svc -v
* Trying 10.233.36.19:80...
* Connected to myweb-svc (10.233.36.19) port 80 (#0)
> GET / HTTP/1.1
> Host: myweb-svc
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Thu, 19 May 2022 15:56:37 GMT
< Content-Length: 28
< Content-Type: text/plain; charset=utf-8
<
Hello World!
myweb-rs-rrxr5
* Connection #0 to host myweb-svc left intact
myweb-svc (10.233.36.19)
여기에 접속하는 것이다.
DNS가 있기 때문에 우리가 별도로 서비스 ip를 외울 필요가 없다.
이것이 kube-dns라는 것이 add-on임에도 불구하고 자동으로 설치되는 이유이다.
'DevOps > Kubernetes' 카테고리의 다른 글
[Kubernetes] Service - Service Discovery (1) (0) | 2022.05.21 |
---|---|
[Kubernetes] Service - ClusterIP (0) | 2022.05.20 |
[Kubernetes] CronJob (크론잡) (0) | 2022.05.20 |
[Kubernetes] Job (잡) (0) | 2022.05.20 |
[Kubernetes] Kubespray로 노드 추가/삭제하기 (0) | 2022.05.20 |
영차영차 성장 블로그
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!