https://kubernetes.io/ko/docs/concepts/services-networking/service/ 참조
쿠버네티스의 파드는 임시로 사용하는 것이기 때문에, 파드가 삭제되고 다시 생성되면 파드의 IP도 바뀐다. 또한 스케일을 줄이면 어떤 것을 남기고 어떤 것을 삭제할지 사용자가 선택할 수 없다. 서비스는 이러한 상황에서 서비스 디스커버리의 역할을 한다.
서비스는 프록시(로드밸런스의 역할도 수행한다), 서비스 디스커버리를 제공해준다. 도커의 netwokr-alias 혹은 link라는 옵션을 이용하면 네트워크에 이름을 붙여서 사용할 수 있다. 서비스는 이것들과 비슷한 기능을 제공한다. IP는 변화할 수 있기 때문에 보통은 이름을 가지고 통신하는 것이 선호되는 방식이다.
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
spec:
ports:
- port: 80 #서비스를 찾아올 포트
targetPort: 8080 #서비스와 연결할 대상
selector: #서비스와 파드 관계는 파드의 레이블과 서비스의 셀렉터로 이루어진다.
app: myapp-rs #서비스가 연결하기 위한 레이블 셀렉터
80번 포트로 들어오면 8080 포트로 내보내준다.
kubetl create -f myapp-svc.yaml #IP는 자동으로 생성된다.
프록시의 IP를 80번으로 요청해서, 프록시와 연결된 3개의 파드에 라우팅한다.
서비스를 생성할 때 같은 이름의 엔드포인트가 자동으로 생성된다. 서비스의 엔드포인트는 파드의 IP와 포트 정보를 가지고 있다. 엔드포인트가 타겟의 정보를 가지고 있다는 의미이다.
엔드포인트는 파드의 상황에 따라 자동으로 수정된다. 엔드포인트는 셀렉터때문에 만들어진다. 셀렉팅 되는 정보를 같은 이름의 엔드포인트에 들고 있다가, 수정사항이 감지되면 자동으로 수정된다.
kubectl run -it nettool --image ghcr.io/c1t1d0s7/network-multitool --rm
# 파드를 명령형 커맨드로 실행한다. 파드의 이름은 nettool이고, --rm 옵션으로 종료시 바로 삭제되도록 하였다.
# 쿠버네티스는 모두 도커의 dettach 모드로 작동한다. -it는 쉘을 실행시켜줘야 하기 때문에 들어간다.
host myapp-svc #IP주소 확인
curl 10.233.43.109 #서비스의 IP로 접속
curl http://myapp-svc #서비스 이름으로 접속
curl http://myapp-svc.default.svc.cluster.local #풀네임으로 접속
#이름 네임스페이스 타입 기본세팅 도메인
Cluster 내부에서만 사용하는 cubedns를 사용한다.
애플리케이션 중 일부(예: 프론트엔드)는 서비스를 클러스터 밖에 위치한 외부 IP 주소에 노출하고 싶은 경우가 있을 것이다.
쿠버네티스 ServiceTypes는 원하는 서비스 종류를 지정할 수 있도록 해준다. 기본 값은 ClusterIP이다.
서비스를 클러스터-내부 IP에 노출시킨다. 이 값을 선택하면 클러스터 내에서만 서비스에 도달할 수 있다. 이것은 ServiceTypes의 기본 값이다.
고정 포트 (NodePort)로 각 노드의 IP에 서비스를 노출시킨다. NodePort 서비스가 라우팅되는 ClusterIP 서비스가 자동으로 생성된다. < NodeIP >:< NodePort >를 요청하여, 클러스터 외부에서 NodePort 서비스에 접속할 수 있다.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-rs
spec:
replicas: 3
selector:
matchLabels:
app: myapp-rs
template:
metadata:
labels:
app: myapp-rs
spec:
containers:
- name: myapp
image: ghcr.io/c1t1d0s7/go-myweb:alpine
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: myapp-svc-np
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 31111
selector:
app: myapp-rs
#모두 접속 가능
curl http://192.168.56.11:31111
curl http://192.168.56.21:31111
curl http://192.168.56.22:31111
curl http://192.168.56.23:31111
기존의 ClusterIP와 Port부분이 다른 것을 볼 수 있다. < Port>:< NodePort>로 되어 있다. 노드의 포트를 열어준다(nodePort 번호로) Node에 이 포트로 접속을 하게 되면 클러스터 외부에서 접근할 수 있다. NodePort는 내부에서도 접근이 가능하다(NodePort = NodePort + ClusterIP). ClusterIP는 내부용이고, NodePort와 LoadBalancer, 혹은 Ingress는 외부에 노출시키기 위해 필요하다.
NodePort의 범위는 30000-32767 사이로만 설정해야 한다. 직접 할당하거나, 랜덤하게 할당되도록 할 수 있다.
클라우드 공급자의 로드 밸런서를 사용하여 서비스를 외부에 노출시킨다. 외부 로드 밸런서가 라우팅되는 NodePort와 ClusterIP 서비스가 자동으로 생성된다.
NodePort 타입의 서비스만으로 클러스터를 외부에 노출시키는 것은 현실적이지 못하다. 이럴 때 사용하는 것이 LoadBalancer 타입이다.
apiVersion: v1
kind: Service
metadata:
name: myapp-svc-lb
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
selector:
app: myapp-rs
EXTERNAL-IP와 NodePort가 모두 활성화되어 있어, 세 가지 방법으로 모두 접속 가능한 것을 확인할 수 있다.
외부의 로드밸런서 장비 없이 로드밸런싱을 가능하게 해 준다. (큰 프로젝트에서는 사용하기 어려움)
MetalLB
OpenELB
값과 함께 CNAME 레코드를 리턴하여, 서비스를 externalName 필드의 콘텐츠 (예:foo.bar.example.com)에 매핑한다. 어떤 종류의 프록시도 설정되어 있지 않다. ClusterIP, NodePort, LoadBalancer와는 완전히 성격이 다르다. 내부에서 외부로 나갈 때 사용한다.
apiVersion: v1
kind: Service
metadata:
name: example
spec:
type: ExternalName
externalName: example.com
kubectl run -it nettool --image ghcr.io/c1t1d0s7/network-multitool --rm
host example #host는 example.com의 실제 IP를 알려준다.
ExternalName을 이용하면 소스코드를 수정할 필요 없이 안정적인 외부용 엔드포인트를 구성할 수 있다. 또한 CNAME 레코드를 생성한다.
때때로 로드-밸런싱과 단일 서비스 IP는 필요치 않다. 이 경우, "헤드리스" 서비스라는 것을 만들 수 있는데, 명시적으로 클러스터 IP (.spec.clusterIP)에 "None"을 지정한다.
쿠버네티스의 구현에 묶이지 않고, 헤드리스 서비스를 사용하여 다른 서비스 디스커버리 메커니즘과 인터페이스할 수 있다.
헤드리스 서비스의 경우, 클러스터 IP가 할당되지 않고, kube-proxy가 이러한 서비스를 처리하지 않으며, 플랫폼에 의해 로드 밸런싱 또는 프록시를 하지 않는다.
apiVersion: v1
kind: Service
metadata:
name: mapp-svc-headless
spec:
clusterIP: None #헤드리스 서비스
ports:
- port: 80
targetPort: 8080
selector:
app: myapp-re-headless
헤드리스 서비스는 스테이트풀셋 컨트롤러가 쓰는 서비스이다.
원래는 서비스의 ClusterIP가 나오지만, 헤드리스 서비스는 파드의 IP가 나온다. 이것을 스테이트풀셋 컨트롤러와 연결시키면 원하는 파드로 이동할 수 있다.