kubernetes service는 app들이 cluster내부, 외부에서의 통신을 가능하게 해준다. 즉, cluster 내부의 pod들끼리의 통신이 가능하게 주고, 동시에 cluster 외부인 user와 외부 cluster 또는 data source에 연결하여 통신이 가능하게 해주는 endpoint가 되는 것이다.
------ --------------- -------------
|User| |Other cluster| |Data Source|
------ --------------- -------------
| | |
| | |
--------------Kubernetes Cluster------------
| ---------- |
| |services| |
| ---------- |
| | |
| | |
| ------ |
| |pod1| |
| ------ |
| | |
| -------------------- |
| | | |
| ---------- ---------- |
| |services| |services| |
| ---------- ---------- |
| | | |
| ------ ------ |
| |pod2| |pod3| |
| ------ ------ |
--------------------------------------------
이렇게 각 pod끼리의 통신을 위해서 service중심으로 pod들이 loose하게 연결되어 통신이 가능하다. 외부와도 마찬가지인데, loose하게 연결된 service를 통해 pod가 외부의 user, cluster, data source에 접근할 수 있고, 외부에서도 cluster에 접근이 가능하다.
그럼 어떻게 외부 사용자가 service를 사용해 우리의 pod에 접속할 수 있는가?
-----user----- ---Cluster Node(192.168.1.2)-----------
|192.168.1.10| --> | |
-------------- | ----pod----- |
| |10.244.0.2| |
| ------------ |
---------------------------------------
user host는 192.168.1.10
에 있고 cluster node는 192.168.1.2
라고 하자. 이들이 같은 network에 있다고 하자. pod는 내부 cluster network에 의해서 ip 10.244.0.2
을 할당받는데, cluster node
와 user host
가 같은 network이므로 user에서 10.244.0.2
에 직접 요청을 보내면 접근이 가능하다.
curl http://10.244.0.2
hello world!
그러나 우리가 원하는 것은 이것이 아니다. pod의 ip에 직접 요청해서 응답을 받고자하는 것이 아니라는 것이다. 왜냐하면 이것이 가능한 이유는 user host가 cluster node와 같은 network라서 가능한 것이다. 만약 다르면 접근이 불가능할 것이다. 따라서, 우리가 원하는 것은 cluster node
를 통해서 pod에 접근하는 방법인데, 192.168.1.2
IP를 사용하여 pod에 접근하고 싶은 것이다.
그래서 중간 매개체인 service
가 이 역활을 해주는 것이다.
-----user----- ---Cluster Node(192.168.1.2)-----------
|192.168.1.10| --> | |
-------------- | ---service--- |
<---------->| 30008 port| |
| ------------- |
| | |
| ----pod----- |
| |10.244.0.2| |
| ------------ |
---------------------------------------
이제 user host는 192.168.1.2
의 30008
port를 통해서 pod에 접근이 가능하다.
curl http://192.168.1.2:30008
hello world!
service
중에도 몇가지 종류가 있는데, 이렇게 cluster내의 pod를 node의 port와 연결하여 외부에서 접근할 수 있도록 만든 것이 바로 nodePort
service이다.
---Cluster Node(192.168.1.2)-----------
| |
| ---service--- |
30008(Node port)<-->| | |
| ------------- |
| 80(service port, port)|
| | |
| | |
| | |
| | |
| 80(target port) |
| ----pod----- |
| |10.244.0.2| |
| ------------ |
---------------------------------------
NodePort
service를 자세히 보도록 하자. NodePort
serviec는 내부 pod를 node의 port에 연결하여 외부에서 접근이 가능하도록 해준다고 했다. 이때 필요한 port들이 몇가지 있는데, 다음과 같다.
이 port들의 네이밍 관점은 service라고 생각하면 쉽다.
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: NodePort
ports:
- targetPort: 80
port: 80
nodePort: 30008
selector:
app: myapp
type: front-end
나머지는 특별히 이전과 다를바가 없고 spec
의 type
과 port
에 집중해보도록 하자. 주의할 것은 nodePort
는 기본적으로 한정된 값이라는 것이다. 따라서 32000보다 큰 수를 연결할 때는 조심해야한다.
마지막으로 pod를 연결하기 위한 selector
이다. 해당 label을 가진 pod라면 pod와 service가 연결된다.
kubectl create -f service-definition.yaml
kubectl get services
curl http://192.168.1.2:30008
사실 service는 반드시 하나의 pod들에만 연결되는 것은 아니다. service를 사용하는 주된 목적 중 하나는 통신도 있지만 'load balancing' 기능도 있기 때문이다.
-------------Cluster Node(192.168.1.2)--------------
| |
| ---service---- |
<---------->| 30008 port | |
| |selector: | |
| | app: myapp| |
| -------------- |
| | |
| | |
| -------------------------------- |
| | | | |
| -----pod1----- -----pod1----- -----pod2----- |
| |10.244.0.2 | |10.244.0.3 | |10.244.0.4 | |
| |labels: | |labels: | |labels: | |
| | app: myapp| | app: myapp| | app: myapp| |
| -------------- -------------- -------------- |
| |
-----------------------------------------------------
service는 외부 사용자의 요청을 pod에 무작위 방식으로 로드를 분산한다. 이러한 로드 밸랜싱으로 부하를 나누는 것이다.
재밌게도, cluster에 여러 개의 node들이 배포된 경우에도 하나의 service로 각 node들에 있는 pod에 대한 로드밸런싱을 수행한다는 것이다.
|------Node1-----| |------Node2-----|
| | | |
| -----------------service---------- |
| | 30008 port | |
| ---------------------------------- |
| | | | | |
| -----pod1----- | | -----pod1----- |
| |10.244.0.2 | | | |10.244.0.3 | |
| |labels: | | | |labels: | |
| | app: myapp| | | | app: myapp| |
| -------------- | | -------------- |
------------------ ------------------
다음과 같이 cluster가 두 개의 node인 node1
, node2
로 이루어져도 service
한개로 이 두 노드 안에 있는 pod를 외부로 연결해주는 기능을 할 수 있는 것이다.
왜 cluster내부의 pod끼리 통신을 할 때 굳이 service가 필요할까? pod끼리의 IP가 이미 cluster 내부에 있기 때문에, 이를 직접 서로 의사소통하면 되지 않을까 생각이 들 것이다.
그런데, pod의 IP는 항상 동일하지 않는다. 만약 pod가 다운되면 pod IP가 달라지게 되고 기존의 IP로는 접근할 수 없게된다. pod가 죽고 살아나는 것은 kubernetes 상에서는 정상적인 동작이기 때문에 이상한 상황이 아니다.
따라서, servcice로 ClusterIP를 받아서 service를 통해 접근하면 pod가 죽어도 변하는 IP를 service가 중간 매개체로 삼아 알려주어 연결이 가능한 것이다. service의 IP는 cluster가 다운되기 전까지 절대 바뀌지 않음으로 이를 이용하여 pod기리 통신을 하는 것이다.
또한, pod가 만약 여러 개인 경우는 어떠한가? redis같은 key-value database기능을 하는 pod들이 n개 있는 경우, 어떤 pod의 IP를 직접 물고와서 통신해야하는가? 이러한 문제는 쉽지가 않다. 그러나 service로 여러 개의 pod들을 연결할 경우, 요청을 연결된 pod 중 하나로 보내어 응답을 전송하는 것이다.
마지막으로 service는 DNS를 제공하는데, 이를 FQDN
이라고 한다. 즉 service의 IP로의 직접접근이 아니라, 이름을 통해 접근할 수 있도록 하여, pod가 service의 IP를 직접 알아낼 필요가 없다. DNS이름만으로 pod가 통신이 가능한 것이다.
service의 이름이 temp이고, namespace가 default이면 다음의 DNS를 갖는다.
temp.default.svc.cluster.local
-------------------Cluster-------------
| ------ ----------- |
| |pod1|<--->| | |
| ------ | | |
| | | |
| ------ | | ------ |
| |pod2|<--->| service | <---> |pod4| |
| ------ | | ------ |
| | | |
| ------ | | |
| |pod3|<--->| | |
| ------ ----------- |
| |
----------------------------------------
다음과 같이 cluster내부에 여러 pod들을 하나의 clusterIP를 가진 service에 연결하여 다른 pod가 이들에 접근할 수 있는 것이다.
pod4는 pod1, pod2, pod3의 IP를 몰라도 되며 service를 통해 통신이 가능하다. 또한, service는 pod1, pod2, pod3에 대한 로드밸런싱 기능을 제공해주고, pod4가 service의 IP가 아닌 DNS, 즉 이름을 통해 접근할 수 있도록 매개체를 지원해준다.
apiVersion: v1
kind: Service
metadata:
name: back-end
spec:
type: ClusterIP
ports:
- targetPort: 80
port: 80
selectopr:
app: myapp
type: back-end
NodePort
와 동일한데, type
이 ClusterIP
라는 것과 nodePort
가 없다는 것이다. 사실 service의 type default가 ClusterIP
이기 때문에 굳이 지명해주지 않아도 된다.
앞서 말했듯이 port는 service기준으로 작성되는 것이다. 따라서 port는 service port이고 targetPort가 pod의 port가 되는 것이다.
마지막으로 selector
를 통해서 label에 맞는 pod를 연결해주도록 한다.
kubectl create -f ./service-definition.yaml
kubectl get services
좀 더 복잡한 cluster예제를 보도록 하자. 다음은 4개의 node가 있고 deployment가 2개 있는데 NodePort service가 30035, 31061로 pod에 외부로 노출되어 있다고 하자.
---Node1--- ---Node2--- ---Node3--- ---Node4---
|192.168. | |192.168. | |192.168. | |192.168. |
|56.70 | |56.71 | |56.72 | |56.73 |
| | | | | | | |
| | | | | | | |
| --------------NodePort 30035------------------ |
| | | |
| ---------------------------------------------- |
| | | | | | | |
| | | | | | | |
| --------------NodePort 31061------------------ |
| | | |
| ---------------------------------------------- |
| | | pod2 | | pod4 | | pod7 | |
| pod1 | | pod3 | | pod5 | | | |
----------- ----------- ----------- ---------
위의 그림처럼 각 Node들은 각자의 host IP를 가지고 있고 NodePort service를 통해서 30035
, 31061
port로 뚫려있다. 그렇다면 user들은 어떻게 pod에 배포된 application에 접근할 수 있는 것을까??
http://192.168.56.70:30035
http://192.168.56.71:30035
http://192.168.56.72:30035
http://192.168.56.73:30035
http://192.168.56.70:31061
http://192.168.56.71:31061
http://192.168.56.72:31061
http://192.168.56.73:31061
다음의 모든 URL을 통해서 요청이 가능하다. 그러나 user는 이렇게 다양한 URL에 관심이 없다. 오직 단일 URL하나만을 원할 뿐이다.
------
|user|
------
|
|
---------------------------
|http://example-vote.com |
|http://example-result.com|
---------------------------
|
Load Balander
|
---------------------------------------------------
---Node1--- ---Node2--- ---Node3--- ---Node4---
|192.168. | |192.168. | |192.168. | |192.168. |
|56.70 | |56.71 | |56.72 | |56.73 |
| | | | | | | |
| | | | | | | |
| --------------NodePort 30035------------------ |
| | | |
| ---------------------------------------------- |
| | | | | | | |
| | | | | | | |
| --------------NodePort 31061------------------ |
| | | |
| ---------------------------------------------- |
| | | pod2 | | pod4 | | pod7 | |
| pod1 | | pod3 | | pod5 | | | |
----------- ----------- ----------- ---------
이룰 위해서 새로운 단일 load balancer를 만들어야하는 것이다. nginx, ha proxy 등이 대표적이다. 하지만 이를 만드는 일은 매우 귀찮고 피곤한 일이다.
이를 위해서 클라우드 공급자(AWS, Azure, GCP)와 같은 cloud에서는 자체 load balancer기능을 제공한다. 이 플랫폼의 load balander를 이용하면 kubernetes service type인 load balander와 연동되어 여러 기능들을 제공받을 수 있다.
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: LoadBalancer
ports:
- targetPort: 80
port: 80
nodePort: 30035
생긴 것 자체는 NodePort
와 같다. 이렇게 type
을 LoadBalancer
로 놓고 배포해놓으면 GCP나 AWS같은 클라우드 공급체가 자신들이 정의한 load balancer로 service를 생성하여 외부에서도 접근할 수 있도록 해준다.
단, 주의할 것은 클라우드 환경이 아닌 경우는 실행되지 않고 에러가 발생할 수 있으니 조심하도록 하자.