Service
는 kubernetes 리소스로 pod에 접근하기 위한 IP주소를 제공한다. pod자체에 IP가 존재하는데 불구하고 Service
를 만들어 사용하는 이유가 무엇일까??
즉, Service
는 동일한 서비스를 제공하는 파드 그룹에 지속적인 단일 접점을 만들려고 할 때 생성하는 리소스인 것이다. 각 서비스는 절대 변경되지 않는 IP
, Port
가 있으며 이는 서비스가 다운되지 않는 한 동일하다. 클라이언트는 IP
와 Port
를 통해서 Service
에 접근할 수 있고, Service
는 연결된 pod
에 요청을 리다이렉트한다.
서비스 하나에 여러 파드들이 연결될 수 있다는 것은, 서비스에 들어온 요청이 연결된 파드들 중 한개로 전달될 수 있다는 이야기이고 이는 즉 로드밸런싱된다는 말이다. 서비스에 파드를 연결하는 것은 label selector를 사용하면 된다.
이제 Service
를 만들어보도록 하자.
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
selector:
app: kubia
ports:
- port: 80
targetPort: 8080
kubia
라는 서비스를 만들고, 해당 서비스의 port
는 80
포트를 연다는 것이다. 그리고 80
포트로 들어온 요청에 대해서는 app=kubia
라는 label을 가진 pod의 8080
포트로 포워딩하겠다는 의미이다.
이제 service를 실행해보도록 하자.
kubectl create -f ./kubia-svc.yaml
service/kubia created
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubia ClusterIP 10.96.5.114 <none> 80/TCP 111s
CLUSTER-IP
는 Service의 IP로 클러스터 내부에 배정된 IP이다. 이는 클러스터 내부에서만 사용할 수 있다는 점에 유의하자. port는 위에서 언급했듯이 80
포트로 열리게 된다.
다음으로 app=kubia
에 연결된 pod가 없으므로 pod를 만들어주도록 한다. 이는 이전에 사용했던 replicaSet
을 사용한다.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: kubia
spec:
replicas: 3
selector:
matchLabels:
app: kubia
template:
metadata:
labels:
app: kubia
spec:
containers:
- name: kubia
image: luksa/kubia
ports:
- name: http
containerPort: 8080
- name: https
containerPort: 8443
이를 실행시키도록 하자.
kubectl create -f ./kubia-replicaset.yaml
replicaset.apps/kubia created
kubectl get po
NAME READY STATUS RESTARTS AGE
kubia-2mjw4 1/1 Running 0 3m9s
kubia-bbshd 1/1 Running 0 3m9s
kubia-xflcp 1/1 Running 0 3m9s
pod들이 배포되었으므로, service
의 cluster-ip에 요청을 보내보도록 하자.
kubectl exec kubia-bbshd -- curl -s http://10.96.5.114
You've hit kubia-xflcp
kubectl exec
을 사용하여 pod내부에서 curl
을 실행한 것은 cluster-ip
가 pod내부에서만 의미있는 ip이기 때문이다. 즉, 클러스터 내부에서만 쓰이는 ip이기 때문이다. 이렇게 요청을 보냈을 때 문제없이 응답이 온 것을 볼 수 있다. 재밌는 것은 kubia-xflcp
가 응답으로 왔는데, kubia service에 대한 요청이 kubia-xflcp
으로 포워드되었다는 것이다. 이는 service
가 하나의 로드밸런싱을 했다고 볼 수 있다.
| <-> kubia-2mjw4
client <---> kubia service <---> | <-> kubia-bbshd
| <-> kubia-xflcp(선택)
클라이언트의 요청은 kubia service를 통해서 연결된 파드에 랜덤하게 보내진다. 만약, 클라이언트의 요청이 응답이 온 파드에서 계속해서 인터렉션이 이루어지길 바란다면 sessionAffinity
를 설정하면 된다. sessionAffinity
는 None
과 ClientIP
라는 두 가지 유형의 서비스 세션 어피니티가 존재한다. ClientIP
로 설정하게되면 같은 클라이언트 IP에 대해서는 하나의 파드를 선정해, 계속해서 동일한 파드에서 클라이언트로 응답을 전달한다.
apiVersion: v1
kind: Service
spec:
sessionAffinity: ClientIP
...
위와 같이 설정하면 서비스 프록시는 동일한 클라이언트 IP의 모든 요청을 동일한 파드로 전달한다.
하나의 service에서 여러 개의 포트를 노출시킬 수 있는데, 이 경우 port의 이름을 지어주어야 한다. 주로 http
, https
용으로 나누어 포트를 열어주곤 한다.
먼저 현재 kubia service를 삭제해주도록 하자.
kubectl delete -f kubia-svc.yaml
service "kubia" deleted
다음으로 다중 포트를 가진 kubia service를 만들어주도록 하자.
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
selector:
app: kubia
ports:
- name: http
port: 80
targetPort: 8080
- name: https
port: 443
targetPort: 8443
ports
부분에 리스트로 name
을 가진 port 두개가 열린 것을 볼 수 있다. 하나는 http
이고 하나는 https
인 것이다. http
는 서비스의 80
포트를 열고 pod의 8080
포트에 연결되며, https
는 서비스의 443
포트를 열고 pod의 8443
포트와 연결된다.
service를 생성하고 이를 확인하면 다음과 같다.
kubectl create -f kubia-svc.yaml
service/kubia created
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubia ClusterIP 10.108.182.101 <none> 80/TCP,443/TCP 4s
80
, 443
포트가 열린 것을 확인할 수 있다. 재미난 것은 두 개 모두 TCP
로 써있는데, 사실 service는 http
로 통신하는 것이 아니라, TCP
, UDP
패킷을 보내는 통신을 한다. 때문에 http의 한 요소인 쿠키 어피니티가 없는 것이다.
포트의 이름을 지정하면 service의 실제 포트가 몇 번으로 열려있는 지와 상관없이 포트 이름으로만으로도 통신이 가능하다. 이는 실제 service와 통신하는데 있어서 클라이언트가 cluster-ip가 아닌 다른 방법으로 접근한다는 것을 의미한다. 왜냐하면 cluster-ip와 port는 어떤 값이 있을 지 클라이언트 입장에서 모르기 때문이다.
이를 위해서 kubernetes는 service가 만들어지고 난 뒤에 클라이언트 pod가 만들어지면, 클라이언트 pod에 대해서 service의 환경변수를 자동으로 설정해준다. 한 가지 유념해야할 것은 service가 먼저 만들어진 후에 클라이언트 pod를 생성해야 한다는 것이다. 테스트를 위해서 pod kubia-2mjw4
를 삭제했다가, 다시 기동되면 환경변수 설정이 어떻게 되었는 지 확인해보기로 하자.
kubectl delete pod kubia-2mjw4
pod "kubia-2mjw4" deleted
이제 kubectl exec
명령어를 이용해서 service에 대한 환경변수가 설정되었는 지 확인하도록 하자.
kubectl exec kubia-hw69s env
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
...
KUBIA_SERVICE_PORT_HTTP=80
KUBIA_SERVICE_PORT_HTTPS=443
KUBIA_PORT=tcp://10.108.182.101:80
KUBIA_PORT_80_TCP_PROTO=tcp
KUBIA_PORT_80_TCP_ADDR=10.108.182.101
KUBIA_PORT_443_TCP=tcp://10.108.182.101:443
KUBIA_PORT_443_TCP_PORT=443
KUBIA_SERVICE_PORT=80
KUBIA_PORT_80_TCP=tcp://10.108.182.101:80
KUBIA_PORT_443_TCP_ADDR=10.108.182.101
KUBIA_SERVICE_HOST=10.108.182.101
KUBIA_PORT_443_TCP_PROTO=tcp
KUBIA_PORT_80_TCP_PORT=80
...
service kubia에 관련된 환경변수들이 설정된 것을 볼 수 있다. KUBIA_SERVICE_HOST
가 kubia service의 cluster-ip이고, KUBIA_SERVICE_PORT
가 port이다. 또한, port
이름 별로 각각 KUBIA_SERVICE_PORT_HTTP
, KUBIA_SERVICE_PORT_HTTPS
가 있는 것을 알 수 있다.
이러한 환경변수를 통해서 서비스에 접근할 수 있지만 사실 좋은 방법은 아니다. 왜냐하면 직접 IP:Port로 접근하는 것이 아니라, DNS로 접근하는 방법이 보다 더 쉽고, 클라이언트 입장에서 직관적이기 때문이다.
kubernetes의 kube-system
namespace안에서는 기본적으로 kube-dns
라는 서비스가 동작하고, 연결된 pod는 동일한 이름의 kube-dns
이다. kube-dns
파드는 dns서버를 실행하며, 모든 pod들은 이 kube-dns
service를 자동으로 사용하도록 구성된다. 이는 각 컨테이너의 /etc/resolv.conf
파일을 수정해 이를 수행하는 것이다. 파드에서 실행 중인 프로세스에서 수행되는 모든 DNS 쿼리는 시스템에서 실행 중인 모든 서비스를 알고 있는 쿠버네티스 자체 DNS서버로 처리된다.
FQDN(정규화된 도메인 이름)을 통해서 service에 접근할 수 있는데, 다음과 같다.
{service-name}.{namespace}.svc.cluster.local
우리의 kubia service는 kubia
라는 이름과 default
라는 네임스페이스를 갖고 있기 때문에 다음과 같이 쓸 수 있다.
kubia.default.svc.cluster.local
service의 cluster-ip, port가 아니라, 해당 domain으로 요청하면 kubia service에 요청하게 되는 것이다.
물론, 이 역시도 클러스터 내부에서만 유효하므로 pod 내부에서 실행해야한다.
kubectl exec kubia-hw69s -- curl http://kubia-svc.default.svc.cluster.local -s
You've hit kubia-bbshd
다음과 같이 DNS를 통해서 클러스터 내의 서비스에 접근 가능한 것을 확인할 수 있다. 더 나아가, service명 자체만으로도 접근이 가능하다.
kubectl exec kubia-r54cp -- curl http://kubia-svc.default -s
You've hit kubia-bbshd
재밌는 것은 서비스 IP로는 ping을 보낼 수 없는데 이는 서비스의 cluster-IP가 가성 IP이므로 서비스 포트와 결합된 경우에만 의미가 있기 때문이다. 이에 대래서는 추후에 더 자세히 알아보도록 하자.
지금까지는 클러스터 내부에서만 접근 가능한 서비스를 만들었기 때문에 pod내부에서만 서비스로 접근이 가능했다.
실제 클러스터를 배포할 때는 서비스를 외부에 노출하여 접근할 수 있도록 해야할 때가 있다. 외부에서 서비스를 접근할 수 있도록 하는 방법은 몇 가지가 있다.
1. NodePort: NodePort
서비스의 경우 각 클러스터 노드는 노드 자체에서 포트를 열고 해당 포트로 수신된 트래픽을 서비스로 전달한다. 즉 node의 IP에서 port를 외부에 열어 해당 포트에 접근한 트래픽을 연결된 서비스에 흘려보내는 것이다. 또한 클러스터 IP와 포트로 클러스터 내부에서도 접근이 가능하다.
2. LoadBalancer: LoadBalancer
는 클라우드 인프라에서 프로비저닝된 전용 서비스로, 트래픽을 모든 노드의 노드포트로 전달한다. 클라이언트는 IP로 서비스에 접근한다. 이는 클라우드 플랫폼을 사용할 때만 가능하다.
3. Ingress resource: Ingress
는 4계층 서비스가 아니라 7계층 서비스를 제공하므로, TLS, mTLS 등과 같은 다양한 기능을 제공할 수 있다. 이에 대해서는 추후에 더 알아보도록 하자.
이 중 가장 쉬운 방법은 NodePort
를 사용하여 노드의 port를 외부에 여는 것이다. NodePort
서비스를 만들면 쿠버네티스는 모든 노드에 특정 포트를 할당하고(모든 노드에서 동일한 포트 번호가 사용됨) 서비스를 구성하는 파드로 들어오는 connection을 전달한다. 만약 노드의 IP가 192.168.10.29이고 port로 8080을 열었다면 192.168.10.29:8080
으로 흘러보내진 트래픽은 NodePort
서비스에 흘러들어가 연결된 파드에 전달되는 것이다.
노드포트 서비스를 생성하는 벙법은 다음과 같다.
apiVersion: v1
kind: Service
metadata:
name: kubia-nodeport
spec:
type: NodePort
selector:
app: kubia
ports:
- port: 80
targetPort: 8080
nodePort: 30123
type
필드를 NodePort
로 정하고 ports
에서 노드에서 외부 포트로 열어줄 nodePort
를 설정해주면 된다. 그러면 nodePort
인 30123포트로 흘러온 트래픽이 서비스의 port
인 80
으로 흐르고, 이는 pod의 8080
포트로 연결된다.
이제 nodePort
를 생성하고 확인해보도록 하자.
kubectl create -f kubia-svc-nodeport.yaml
service/kubia-nodeport created
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubia-nodeport NodePort 10.105.180.225 <none> 80:30123/TCP 2m2s
...
PORT(S)
부분을 확인하면 80:30123/TCP
로 쓰여있다. 이는 node의 30123
포트가 외부에 열려있다는 것이다. 또한, 아까 말했듯이 NodePort
는 모든 노드에서 포트가 열린다고 했다. 따라서 모든 노드의 30123
포트가 열려있게 된다. 따라서, 해당 NodePort
service에 접근할 수 있는 방법은 다음과 같다.
필자의 경우 node1에 대한 ip를 설정해놓았다.
cat /etc/hosts
127.0.0.1 localhost
172.31.13.93 master
172.31.2.117 node1
172.31.4.174 node2
요청을 보내보도록 하자.
curl node1:30123
You've hit kubia-r54cp
응답이 온 것을 확인할 수 있다. 이처럼 nodeip:port로 외부에 서비스가 연결된 것을 확인할 수 있다.
로드밸런서를 이용해서 외부 서비스를 노출하는 방법은 다루지 않는다. 로드밸런서는 EXTERNAL-IP가 따로 존재하고 port를 외부에 노출시켜 EXTERNAL-IP:PORT로 외부에서 접근할 수 있도록 해준다. 즉, 노드IP:Port가 아니라, 제 3의 다른 IP를 사용하는 것이다. 다만, 로드밸런서는 클라우드 공급자 이외에 사용할 수 없다.
마지막으로 Ingress
resource이다. 로드밸런서는 public IP를 통해 포트마다 서비스를 열어야하는 불편함이 있지만 ingress의 경우는 한 IP로 수십 개의 서비스에 접근이 가능하도록 지원해준다. 이는 요청한 호스트와 경로에 따라 요청을 전달할 서비스를 결정하기 때문이다.
ingress는 application layer에서 HTTP로 작동하기 때문에 4계층에서 동작 중인 서비스에서는 없는 쿠키 기반의 세션 어피니티 등과 같은 기능을 제공할 수 있다.
ingress object는 사실 클러스터 외부에서 내부로 접근하는 요청을 어떻게 처리할 지 정의한 규칙일 뿐이다. 이 규칙에 따라 트래픽을 분산하는 것이 바로, ingress controller이다. 따라서 ingress controller가 먼저 있어야 ingress object가 동작하게 된다. ingress controller는 기본적으로 kubernetes cluster에 설치되지 않으므로, 설치가 필요하다. https://kubernetes.github.io/ingress-nginx/deploy/
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml
위의 명령어를 사용하면 다음의 결과가 나오게 된다.
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
kubectl get po -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-sblll 0/1 Completed 0 59s
ingress-nginx-admission-patch-p72x7 0/1 Completed 0 59s
ingress-nginx-controller-8558859656-lc2wb 1/1 Running 0 59s
ingress-nginx-controller
pod가 생성된 것을 볼 수 있다. ingress controller는 service와 pod 등 각종 kubernetes resource를 관리하는 하나의 object인데, ingress resource와의 관계는 다음과 같다.
<--kubia.example.com/kubia <--> |-----------ingress-controller-----------| <--> service1 <--> pod1
client <--kubia.example.com/foo <--> | service(NodePort, LoadBalancer) -> pod | <--> service2 <--> pod2
<--foo.example.com <--> | -> ingress rule | <--> service3 <--> pod3
<--bar.example.com <--> |----------------------------------------| <--> service4 <--> pod4
client는 호스트와 path와 따라 외부에 노출된 서비스(NodePort, LoadBalancer)에 요청을 보낸다. 이때 service는 ingress-controller가 관리하는 service로 해당 service로 라우팅된 요청은 ingress-controller가 만들어낸 ingress-controller pod에 요청이 전달된다. pod에서는 ingress resource를 읽어서 정해진 rule에 따라 맵핑된 service로 트래픽을 전달한다.
이제 Ingress resource를 만들어보도록 하자.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kubia-ingress
spec:
ingressClassName: nginx
rules:
- host: kubia.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kubia-nodeport
port:
number: 80
ingressClassName
는 ingress controller의 class이름을 적어주어야 한다.
kubectl get ingressclasses.networking.k8s.io
NAME CONTROLLER PARAMETERS AGE
nginx k8s.io/ingress-nginx <none> 50m
nginx
로 ingressclass resource가 있는 것을 확인할 수 있다. 이를 ingress yaml파일의 ingressClassName
에 적어주면 된다.
rules
가 ingress에 적용할 규칙들을 말한다. host로는 kubia.example.com
로 접근하고 path
는 /
이다. 해당 호스트와 path조건이 맞으면 ingress controller는 kubia-nodeport의 80포트로 트래픽을 흘려보낸다.
kubectl create -f kubia-ingress.yaml
Error from server (InternalError): error when creating "kubia-ingress.yaml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": failed to call webhook: Post "https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s": context deadline exceeded
ingress를 만들어내면 다음과 같은 에러가 생길 수 있다. 이는 ingress에 대한 spec사항이 변경되면서 ingress-nginx-controller에서 이를 반영하지 않아 발생하는 문제로 확인되고 있다. 따라서, validation에 쓰이는 일부 오브젝트를 삭제할 필요가 있다.
kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
validatingwebhookconfiguration.admissionregistration.k8s.io "ingress-nginx-admission" deleted
kubectl create -f kubia-ingress.yaml
ingress.networking.k8s.io/kubia created
validatingwebhookconfiguration을 삭제 후 ingress을 생성하려고 하면 성공적으로 생성된다. 사실 왜 이런 오류가 발생하는 지에 대해서는 잘 모르겠다. ingress-nginx-controller가 실제 배포에 쓰이지 않아, 이런저런 오류들이 많이 쌓여있는 것 같다. 실무에서는 nginx-ingress-controller 또는 istio, kong ingress controller들을 사용하도록 하자.
잘 만들어졌는 지 확인해보도록 하자.
kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
kubia nginx kubia.example.com 80 4m35s
제대로 만들어진 것을 확인하였다.
이제 요청을 보내야하는데, 필자의 경우는 EC2 내부에 kubernetes cluster를 구축한 것이기 때문에 IngressController가 LoadBalancer와 연결되지 않았기 때문에 외부 IP가 없다. 또한, NodePort로도 열지 않았기 때문에, 따로 port forwarding으로 ingress-nginx-controller의 외부 서비스를 만들어주도록 하자.
kubectl port-forward --namespace=ingress-nginx service/ingress-nginx-controller 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
ingress-nginx-controller
를 외부에서 접근할 수 있도록 포트 포워딩시켰으므로, 다른 터미널을 열어서 ingress 정의에 따라 요청을 보내보도록 하자.
curl http://localhost:8080 -H "host: kubia.example.com"
You've hit kubia-g9wql
8080
으로 ingress-nginx-controller에 대한 service를 외부에 열었으므로, 다음과 같이 접근할 수 있는 것이다. ingress-nginx-controller는 들어온 호스트와 path를 바탕으로 ingress Rule과 비교하여 어떤 service로 들어온 트래픽을 전달할 지 결정하는 것이다.
ingress에는 여러 가지 rule을 결정할 수 있는데, 동일한 호스트에 서로 다른 경로를 매핑할 수도 있다. 현재의 경우 필요한 서비스들을 만들지 않아서 동작하진 않지만 다음과 같이 만들 수 있다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kubia-ingress
spec:
ingressClassName: nginx
rules:
- host: kubia.example.com
http:
paths:
- path: /kubia
pathType: Prefix
backend:
service:
name: kubia-nodeport
port:
number: 80
- path: /bar
pathType: Prefix
backend:
service:
name: bar
port:
number: 80
이 경우 kubia.example.com
라는 같은 호스트를 가지지만 path가 /kubia
, /bar
로 다르다. 따라서 다음과 같이 요청을 보내야한다.
curl http://localhost:8080/kubia -H "host: kubia.example.com"
curl http://localhost:8080/bar -H "host: kubia.example.com"
이와 같이 ingress는 같은 호스트에 다양한 path를 가질 수 있으며, 반대로 같은 path에 다양한 host를 가질 수 있도록 rule을 정할 수 있다. 더불어 ingress는 http 계층에서 동작하기 때문에 TLS를 설정할 수 있다. 그러나 일반적으로 ingress는 cluster내부의 서비스를 통해 백엔드 pod에게 traffic을 흘려보내는 것이므로 굳이 TLS를 지원하지 않아도 된다. 다만 ingress-controller의 경우는 외부에 노출되기 때문에 TLS를 제공하여 HTTPS로 접근하도록 만드는 것이 좋다.
readiness probe는 liveness probe와 마찬가지로 pod의 컨테이너를 확인하는 로직이다. liveness probe가 container가 제대로 동작하고 있는 지를 확인하기 위함이라면 readiness probe는 pod의 container가 준비가 되어있는 지 확인하기 위함이다.
pod가 실행되고 서버가 실행되며 일련의 데이터를 처리하는 로직이 필요할 수 있다. 이러한 setup이 이루어지기 전에 service에 의해 외부의 요청이 전달되면 일련의 setup과정에서 큰 오류를 만들 수 있다. 이런 일이 없도록 readiness probe는 개발자가 지정한 작업을 수행하여 readiness가 준비되었다고 판단할 때, service로 부터 traffic을 받는다. 즉, readiness probe에서 작업이 완료되지 않으면 service에서 해당 pod를 연결하지 않는다는 것이다.
재밌는 것은 liveness probe는 pod의 liveness에 문제가 생기면 pod를 계속해서 죽였다 살린다. 즉, 새로운 pod로 갈아치운다. 반면에 readiness probe는 readiness check에서 정상성이 확보되지 않아도 pod를 죽이지 않고, service와의 연결을 끊는다. 이후에 다시 readiness probe에 의해 readiness check가 성공한다면 service에 연결하여 외부의 traffic을 받을 수 있게 된다. 따라서 3개의 pod에서 1개만이 readiness probe에 실패하였다면 1개만 service에 연결되지 않고 나머지 2개는 연결된다는 것이다. 이후 시간이 지나 readiness probe에 실패했던 container가 성공하면 pod를 service에 연결한다.
readiness probe의 유형은 liveness probe와 마찬가지로 3가지가 있다.
이전에 만들었던 ReplicaSet
에 readinessProbe를 설정해주도록 하자.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: kubia
spec:
replicas: 3
selector:
matchLabels:
app: kubia
template:
metadata:
labels:
app: kubia
spec:
containers:
- name: kubia
image: luksa/kubia
ports:
- name: http
containerPort: 8080
- name: https
containerPort: 8443
readinessProbe:
exec:
command:
- ls
- /var/ready
다음의 yaml파일에서 containers.readinessProbe
부분을 확인할 수 있다. exec
을 사용하여 명령어를 실행하도록 하였는데, ls /var/ready
를 실행하도록 하는 것이다. 기본적으로 /var/ready
는 없는 디렉터리이므로 readiness probe에 실패하게된다. 다음의 ReplicaSet을 만들어보도록 하자.
kubectl create -f ./kubia-replicaset.yaml
replicaset.apps/kubia created
kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
default kubia-8qxmr 0/1 Running 0 4s
default kubia-bk96x 0/1 Running 0 4s
default kubia-jkk7t 0/1 Running 0 4s
READY
부분에 0/1
이 표시된 것을 볼 수 있다. 아직 readiness probe의 check에서 실패한 것이기 때문이다. 이 상태에서는 위에서 ingress에 연결된 ingress controller로 요청을 보내어도 응답이 오지 않는다. 이유는 kubia-nodeport
에 연결된 kubia-* pod가 없기 때문이다. 이는 readiness check에 실패하였기 때문에 service에 pod가 등록되지 않은 것이다.
ready상태로 만들기 위해서 pod에 접근해 /var/ready
디렉터리르 만들어주도록 하자.
kubectl exec kubia-8qxmr -- touch /var/ready
다음으로 시간이 지난 뒤 정말 ready상태가 되었는 지 확인해보도록 하자.
kubectl get po
NAME READY STATUS RESTARTS AGE
demo-b66f56cf5-qg9wv 1/1 Running 0 64m
kubia-8qxmr 1/1 Running 0 51s
kubia-bk96x 0/1 Running 0 51s
kubia-jkk7t 0/1 Running 0 51s
READY
가 1/1
이 된 것을 확인할 수 있다! 이제 ingress-controller를 통한 요청에도 문제없이 응답이 올 것이다. 이는 kubia-nodeport
svc에 pod가 연결되었기 때문이다.
위의 예제는 readiness probe를 설정하기 위한 예제일 뿐이며, 실제로는 서버가 client 요청을 받을 수 있을 단계가 되면 특정 path로 GET요청을 보내도록 readiness probe에게 만들면 된다. 이는 전적으로 개발자의 몫이다.
두 가지 주의해야할 사항이 있다.
1. readiness probe는 선택이 아닌 필수다. 만약 readiness probe를 만들지 않으면 pod가 시작하자마자 service에 연결되어 endpoint로 등록되는데, 이는 pod가 client의 요청을 받지 못할 초기 init 상황에서 client는 Connection refused
에러를 받을 수 있기 때문이다.
2. pod가 종료될 때 연결된 service들에 대해서 연결을 종료하기 위해 readiness probe에 exit code를 넣을 수 있다. 그러나 이러한 짓은 하지 말도록 하자. 어차피 pod가 종료되면 연결된 서비스들에 대한 endpoint등록이 모두 말소되기 때문에 사후 처리를 신청쓰지 않아도 된다.