[K8s]5-2. 서비스(Service)
in etc on Kuberenetes
이 글은 쿠버네티스 인 액션 이라는 책과 검색을 통해서 공부한 것을 정리합니다.
서비스
서비스 검색
서비스를 생성해서 포드에게 액세스할 수 있는 단일 IP 주소와 고정된 포트를 가질 수 있다. 서비스의 라이프사이클 동안 변경되지 않는다. 결국 서비스의 단일 IP 주소 및 고정 IP 주소를 통해서 포드에 항상 액세스할 수 있음.
쿠버네티스는 클라이언트의 포드가 서비스의 IP/포트 를 알아낼 수 있는 방법을 제공함.
(1) 환경 변수를 이용한 서비스 검색
- 쿠버네티스는 포드가 시작되면, 그 순간에 존재하는 각 서비스를 가리키는 환경 변수 세트를 초기화 함.
- 포드는 환경 변수를 조사해서 서비스의 IP 주소와 포트 번호를 알아낼 수 있음.
- 환경변수 조사
➜ kubectl exec kubia-svc-client env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=kubia-svc-client
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBIA_PORT_80_TCP_PROTO=tcp
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBIA_SERVICE_HOST=10.98.199.212 # '서비스' 의 클러스 IP
KUBIA_PORT_80_TCP_PORT=80 # 사용가능한 포트
(생략)
(2) DNS 를 통해서 IP를 찾는 방법
FQDN(정규화된 도메인 이름)을 통해서 액세스 가능하다
- ex) backend-database.default.svc.cluster.local
backend-database
는 서비스 이름default
는 서비스가 정의된 네임스페이스svc.cluster.local
는 모든 클러스터의 로컬 서비스 이름에 해당하는 클러스터의 도메인 접두사
➜ kubectl get pods
NAME READY STATUS RESTARTS AGE
kubia-svc-client 1/1 Running 0 150m
kubia-svc-not-client 1/1 Running 0 143m
➜ kubectl exec -it kubia-svc-client bash
root@kubia-svc-client:/# curl http://kubia
You've hit kubia-svc-not-client
root@kubia-svc-client:/# curl http://kubia.default
You've hit kubia-svc-not-client
root@kubia-svc-client:/# curl http://kubia.default.svc.cluster.local
You've hit kubia-svc-not-client
클러스터 외부 서비스에 연결
수동으로 서비스 엔드포인트 설정
external-service.yml
1 apiVersion: v1 2 kind: Service 3 metadata: 4 name: external-service 5 spec: 6 ports: 7 - port: 80 # 80으로 들어오는 연결을 처리하는 external-service 이라는 서비스
external-service-endpoint.yml
1 apiVersion: v1
2 kund: Endpoints
3 metadata:
4 name: external-service # 엔드포인트 객체 이름은 서비스 이름과 매칭돼야
5 subsets:
6 - addresses:
7 - ip: 11.11.11.11
8 - ip: 22.22.22.22 # 엔드포인트 IP들
9 ports:
10 port: 80 # endPoint의 대상 포트 80 함
- Endpoint의 객체 즉 name 은 서비스와 동일한 이름이어햐 함.
FQDN(정규화된 도메인 이름)으로 외부 서비스를 참조
exrernal-service-externalname.yml
1 apiVersion: v1
2 kind: Service
3 metadata:
4 name: external-service
5 spec:
6 type: ExternalName # 반드시 서비스 타입은 ExternalName으로 설정
7 externalName: someapi.somecompany.com # 실제 서비스의 전체 도메인 주소
8 ports:
9 - port: 80
ExternalName 서비스
는 DNS 레벨에서만 구현됨.- 서비스로 연결하는 클라이언트는 서비스 프록시를 통하지 않고, 직접 외부 서비스로 연결할 것이다.
외부 서비스에서 외부 클라이언트로
설명
- 예를 들어서 외부 서비스(웹서버)를 외부에 노출해서 외부 클라이언트가 접근할 수 있도록 함.
NodePort
- 쿠버네티스가
모든 노드를 대상으로 포트를 예약
함 - 그리고
들어오는 접속을 서비스 각 부분의 포트로 전송
함. ClusterIP(오직 클러스터 내부에서만 접근)와 큰 차이는 내부 클러스터 IP를 통해 액세스도 되고, 노드의 IP와 예약된 포트를 통해서도 액세스 된다.
kubia-svc-nodeport.yml
1 apiVersion: v1
2 kind: Service
3 metadata:
4 name: kubia-nodeport
5 spec:
6 type: NodePort
7 ports:
8 - port: 80 # 서비스의 내부 클러스터 IP 포트
9 targetPort: 8080 # 이 서비스를 지원하는 포드의 대상 포트
10 nodePort: 30123 # 이 서비스는 각 클러스터 노드를 포트 30123을 통해 액세스함.
11 selector:
12 app: kubia
$ get svc kubia-nodeport
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubia-nodeport NodePort 10.98.18.230 <none> 80:30123/TCP 26s
실습
- 노드의 IP를 알아내고 > $ curl http://알아낸노드IP:30123
LoadBalancer
- NodePort에 로드밸런서 기능을 넣은 서비스로 본다.
- 대게 많은 쿠버네티스 클러스터는 로드 밸런서를 자동으로 프로비저닝하는 기능을 지원함.
그래서 쿠버네티스 클러스터 기능을 지원한다면 NodePort 서비스보다 LoadBalancer 서비스를 생성하여 자동으로 프로비전 할 수 있음.
kubia-svc-loadbalancer.yml
1 apiVersion: v1
2 kind: Service
3 metadata:
4 name: kubia-loadbalancer
5 spec:
6 type: LoadBalancer
7 ports:
8 - port: 80
9 targetPort: 8080
10 selector:
11 app: kubia
- NodePort와 다르게
특이점은 노드를 위한 포트를 설정하지 않음
쿠버네티스가 알아서 선택하게 함.
실습
$ kubectl get svc kubia-loadbalancer > EXTERNAL-IP
$ curl http://{EXTERNAL-IP}
인그레스(Ingress)
- 여러개의 서비스를 하나의 인그레스로 외부에 노출 할 수 있음.
- 실습을 위해서 minikube를 사용하고 있으면,
➜ minikube addons enable ingress
✅ ingress was successfully enabled
➜ minikube addons list
- addon-manager: enabled
- dashboard: enabled
- default-storageclass: enabled
- efk: disabled
- freshpod: disabled
- gvisor: disabled
- heapster: disabled
- ingress: enabled
- logviewer: disabled
- metrics-server: disabled
- nvidia-driver-installer: disabled
- nvidia-gpu-device-plugin: disabled
- registry: disabled
- registry-creds: disabled
- storage-provisioner: enabled
- storage-provisioner-gluster: disabled
kubia-ingress.yml
1 apiVersion: extension/v1beta1 2 kind: Ingress 3 metadata: 4 name: kubia 5 spec: 6 rules: 7 - host: kubia.example.com 8 http: 9 paths: 10 - path: / # kubia.example.com/ 서비스로 라우팅됨 11 backend: 12 serviceName: kubia-nodeport 13 servicePort: 80 14 #- path: /foo # kubia.example.com/foo 서비스로 라우팅됨 15 # backend: 16 # serviceName: kubia-foo 17 # servicePort: 80
- kubia.example.com 으로 전달되는 요청은 80포드의 kubia-nodeport 라는 서비스로 보내짐.
HTTP 요청이 인그레스 컨트롤러를 통해 수신되는 규칙임.
$ kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
kubia kubia.example.com 10.0.2.15 80 174m
- etc/hosts 파일 수정. 위에서 얻은 ADDRESS
10.0.2.15 kubia.example.com
readiness Probe
- 포드를 서비스의 엔드포인트에 포함시킬지의 여부를 결정하기 위해서 사용한다.
- 레디네스 프로브는 주기적으로 호출이 되어서, 포드가 클라이언트의 요청의 수락을 받아드리는 여부를 결정함.
- 결국 클라이언트의 요청에 대한 체크리스트로 이해하는 것이 좋음.
- 각 컨테이너에 국한된 것.
liveness vs readiness
- 라이브니스 프로브는 주기적으로 호출하여 상태가 좋지 않은 컨테이너를 종료하고 새로운 것으로 교체하여 포드의 상태를 좋게 유지함.
레디니스 프로브는 오직 포드가 요청을 수신할 수 있는 환경인지를 파악하여, 포드가 요청을 수신할 수 있는 환경이 됐을 때 수신한다.
- 아래는 readiness의 종류인데 liveness 와 동일하므로 실습은 생략
(1) Exec Probe
(2) HTTP Get Probe
(3) TCP Socket Probe
주의 할 점
- 항상 레디네스(readiness Probe)를 정의 할 것.
- 정의 하지 않으면 거의 즉시 서비스의 엔드포인트가 되게 된다.
- 간단한 HTTP 요청을 보내더라도 레디네스 프로브를 항상 정의해야 함.
헤드리스(headless) 서비스
- 클라이언트가 모든 포드에 연결해야 한다면? = 대개 클러스터링 구성의 경우..ex)카프카, ignite 등..
- 이전까지는 서비스 연결은 임의로 선택된 하나의 포드로 전달되고, 서비스는 안정적인 IP 주소를 제공하는 방법을 사용했다.
- 즉, 일반적으로 쿠버네티스는 클라이언트가 DNS 룩업을 통해서 포드의 IP를 찾는 것을 허용한다. 대개 DNS 룩업을 실행하면 서버는 단일 IP(ClusterIP)를 반환한다.
- 그러나 ClusterIP : None 으로 설정하게 되면(이말은 쿠버네티스에게 클러스터 IP가 필요없다고 말하는 것) 포드의 IP 목록을 전부 알려줄 것임. 따라서 클라이언트는 한 개, 혹은 그 이상 혹은 전부를 연결할 수 있다.
헤드리스 서비스 실습
- kubia-svc-headless.yml
1 apiVersion: v1 2 kind: Service 3 metadata: 4 name: kubia-headless 5 spec: 6 clusterIP: None 7 ports: 8 - port: 80 9 targetPort: 8080 10 selector: 11 app: kubia
➜ kubectl create -f kubia-svc-headless.yml
service/kubia-headless created
➜ kubectl describe svc kubia-headless
Name: kubia-headless
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=kubia
Type: ClusterIP
IP: None
Port: <unset> 80/TCP
TargetPort: 8080/TCP
Endpoints: 172.17.0.2:8080
Session Affinity: None
Events: <none>
- dns 룩업을 위한 파드 생성
➜ kubectl run dnsutils --image=tutum/dnsutils --generator=run-pod/v1 --command -- sleep infinity
➜ kubectl exec dnsutils nslookup kubia-headless
(생략)
Name: kubia-headless.default.svc.cluster.local
Address: 172.17.0.2
- 여기서 주목해야하는 것은
Address: 172.17.0.2
이다. 이는 해당 IP가 준비되었다고 하는 것이며, 나는 어떤 pod 가 이 ip에 해당하는지 살펴보려고 한다. - 그리고 따로 서비스에 아래와 같이 설정하지 않으면 현재 running 상태의 준비된 pod만 노출 된다는 것을 명심하자.
(구 버전)
kind: Service
metadata:
annotations:
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
(신 버전)
spec 정의의 publishNotReadyAddresses 의 서비스의 스펙 필드를 공식문서에서 살펴본다.
pod 상태 자세히보기
➜ get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
dnsutils 1/1 Running 0 6m32s 172.17.0.9 minikube <none> <none>
kubia-svc-client 1/1 Running 2 9d 172.17.0.4 minikube <none> <none>
kubia-svc-not-client 1/1 Running 2 9d 172.17.0.2 minikube <none> <none>
kubia-svc-not-client
현재 running 상태의 pod가 해당 ip임을 알 수 있는데…. 즉. DNS 서버는 kubia-headless.default.svc.cluster.local FQDN을 위해서172.17.0.2
인kubia-svc-not-client
의 준비된 POD를 리포팅한 것이다.
결론
- 클라이언트 관점에서 엔드포인트 파드에 연결하는건 다르지 않음.
- 논헤드리스 서비스나 헤드리스 서비스나 결국에는 서비스의 DNS 이름으로 연결하고 최종적으로 포드에 접속하게 된다.
- 그러나
헤드리스 서비스
에서 DNS는 포드의 IP를 직접 반환. 위처럼 172.17.0.2 라는 IP를 반환하기 때문에 클라이언트는 서비스 프록시 대신 포드에 직접 연결한다.
번외. 논헤드리스 서비스(서비스의 ClusterIP)가 반환하는 IP를 알아보자.
➜ minikube kubectl exec dnsutils nslookup kubia
(생략)
Name: kubia.default.svc.cluster.local
Address: 10.98.199.212
10.98.199.212
이 녀석에 대해서 알아보자
➜ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
dnsutils 1/1 Running 0 12m 172.17.0.9 minikube <none> <none>
kubia-svc-client 1/1 Running 2 9d 172.17.0.4 minikube <none> <none>
kubia-svc-not-client 1/1 Running 2 9d 172.17.0.2 minikube <none> <none>
10.98.199.212
이 IP 녀석이 안보이는데?
- 반환된 IP는 아래와 같이
kubia name의 서비스
의클러스터 IP
였다.
➜ kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11d <none>
kubia ClusterIP 10.98.199.212 <none> 80/TCP 9d app=kubia
kubia-headless ClusterIP None <none> 80/TCP 28m app=kubia
kubia-nodeport NodePort 10.98.18.230 <none> 80:30123/TCP 5d1h app=kubia