Workload Resource
StatefulSet
Pain point
- Deployment 의 Pod 는 “일용직” 과 유사한 측면이 있다.
- 일용직 노동자는 일을 그만두어도 다른 인력을 쉽게 구해 대체할 수 있듯이,
- Pod 는 삭제되어도 아무런 영향을 주지 않고 Deployment 에 의해 재생성된다.
- 하지만 회사의 중요한 업무를 맡는 직책 (예를 들면 대표이사) 라면?
- Deployment 의 Pod 는 이름을 예측할 수 없다.
- Deployment 를 생성하여 Pod 를 생성하면
nginx-76769d88f7-gsmjb
와 같이 deployment 이름 뒤에 replicaset 과 pod 를 구분짓는 임의의 문자열이 생성되어 작명된다. - 하지만 이름을 예측해야 할 필요성이 있다면?
- Deployment 를 생성하여 Pod 를 생성하면
StatefulSet 이란?
- Pain point 1 번의 설명처럼, Deployment 의 Pod 는 언제든지 삭제되고 재생성되어도 문제가 없는 애플리케이션을 위한 것이다.
- 이것을 Stateless 라고 표현한다.
- 하지만 어떤 애플리케이션의 경우 삭제되거나 재생성되는 것이 영향력을 가질 수 있고, 이러한 애플리케이션을 위한 것이 StatefulSet 이다.
- StatefulSet 의 파드를 이전에는 “Pet” 이라고 표현했다. 다른 것으로 대체될 수 없는 소중한 것이라는 의미를 전달하기 위해서이다.
- StatefulSet 은 대표적으로 다음과 같은 특징을 갖는다.
- StatefulSet 이 생성한 파드에는 임의의 문자열이 아닌 0부터 시작하는 숫자가 부여된다. 즉,
foo
라는 이름의 StatefulSet 을 생성하면foo-0
,foo-1
… 등의 이름이 부여된다.- 이것은 StatefulSet 이 생성하는 Pod 가 소중한 것이라는 개념을 뒷받침해준다. 자신이 기르는 애완동물 이름을 랜덤하게 지어주는 사람은 없다.
- StatefulSet 의 Pod 들은 소중하기 때문에 일반적으로 데이터가 안전하게 보관되어야 할 필요가 있다. 따라서 StatefulSet 은 PVC (쿠버네티스의 볼륨 요청 방식, 뒤에서 다룬다) 를 자동으로 생성해주는 기능을 제공한다.
- StatefulSet 이 생성한 파드에는 임의의 문자열이 아닌 0부터 시작하는 숫자가 부여된다. 즉,
Usecase
- Database
- 대다수의 DBMS 들은 고가용성 설계를 할 때 단순히 데이터 복제를 하진 않는다.
- 다양한 방식으로 고가용성 DBMS 가 운영되지만 하나의 사례는 여러개의 DB 인스턴스 중 하나를 Master (혹은 Active), 나머지를 Slave (혹은 Standby) 로 선정하고 Master 가 모든 요청을 처리하되, 데이터 저장 / 변경의 경우 일정 부분의 Slave 에도 반영이 되어야 요청이 처리된 것으로 간주하는 방식이다.
- 이렇게 하면 모든 DB 인스턴스에 정보를 복제하는 것보다 빠르게 요청을 처리할 수 있고, 데이터 이중화라는 본래의 목적도 달성할 수 있기 때문이다.
- 하지만 이때에는 DB 인스턴스가 삭제되는 것이 가볍게 지나칠 일이 아니기에 함부로 삭제되거나 재생성되어서는 안된다.
- 또한, DB 의 사용자(클라이언트) 입장에서는 Master 에게 요청을 보내야 하기 때문에 어떤 인스턴스가 Master 인지 알아야 한다. 즉, DB 인스턴스의 이름을 사전에 알고 있어야 된다는 이야기이다.
DaemonSet
Pain point
- 어떤 애플리케이션은 모든 노드에서 작동해야 할 필요가 있다.
- 만일 Deployment 를 이용해 파드를 모든 노드에서 작동하게 하려면, 추가적인 설정이 필요할 뿐더러 클러스터에 노드가 추가되었을 경우 파드의 개수를 수동으로 증가시켜줘야 한다.
DaemonSet 이란?
- DaemonSet 은 모든 노드 각각에 반드시 한개의 Pod 가 작동하도록 보장하는 리소스이다.
Usecase
- 노드를 관리하는 애플리케이션
- 노드의 Networking 을 담당하는 파드
- 쿠버네티스 시스템 컴포넌트 중 노드 내외부의 통신을 담당하는 Kube-proxy 도 DaemonSet 으로 생성된다.
- 노드의 로그를 수집하는 파드
- 노드의 시스템 로그 및 파드들이 생산하는 로그를 수집하는 파드의 경우 모든 노드에 배포되게 하기 위해 DaemonSet 으로 생성된다.
- 노드의 Networking 을 담당하는 파드
Job
Pain point
- 파드가 생성된 후 일정한 작업을 수행하고 종료되었으면 좋겠다.
- 하지만, Deployment 는 파드가 삭제되어도 재생성되기에 이러한 니즈를 충족시킬 수 없다.
Job 이란?
- 파드를 생성한 후, 작업이 정상적으로 종료될때까지 파드를 유지하다가 정상 종료 이후 삭제하는 리소스이다.
Usecase
- 일회성 작업
- 예를 들면, AI/ML 모델 학습용 파드나
- 시스템 성능 테스트 (벤치마크) 파드 등
CronJob
Pain point
- 주기적으로 어떤 작업을 수행해주기 위해, Job 이 특정한 시간 주기로 생성되었으면 좋겠다.
CronJob 이란?
- 지정한 시간 간격 (혹은 규칙) 에 따라 Job 을 생성하여 특정한 작업을 하게 해주는 리소스이다.
Usecase
- 주기적인 작업을 요하는 애플리케이션
- 예를 들면, 매월 1일자에 저장된 로그를 압축하여 디스크 사용량을 줄이는 파드
Networking Resource
네트워크 관련 리소스들은 아래의 Deployment 를 활용한 실습을 통해 소개드리겠습니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:stable
Service
Pain point
- Deployment 를 통해 파드를 여러개 생성했을 때, 단일 진입점이 있었으면 좋겠다.
- Pod IP 와 같이 언제든 바뀔 수 있는 진입 주소가 아닌, 고정된 진입 주소가 있으면 좋겠다.
Service?
- Service 는 Pod 로 접근하는 엔드포인트를 제공하는 리소스이다.
- 위의 Pain Point 는 이 Service 리소스를 통해 다음과 같이 해결된다:
- Service 로 들어온 트래픽은 Service 와 연결된 Pod 들로 부하 분산된다.
- Service 는 생성시에 Virtual IP 가 할당되기에 접근할 수 있는 고정주소로 사용할 수 있다.
- 추가설명) Pod 는 재생성이 빈번하고 그때마다 IP 를 새로 할당받지만, Service 의 경우에는 재생성될 일이 적고, 생성시에 IP 를 할당받기는 하지만 YAML 파일에 IP 를 직접 명시해 줄 수도 있다. 또한, 쿠버네티스에는 클러스터 내부에서 사용할 용도의 DNS 가 존재하기에, Service 의 IP 주소가 아닌 도메인으로 접속하는 것도 가능하다.
- 이것에 관해서는 아래의 실습에서도 확인할 수 있다.
ClusterIP Service
ClusterIP 서비스란?
- 클러스터 내부 통신용 엔드포인트를 생성하기 위한 서비스 리소스이다.
- Service 리소스를 생성할 때,
type
을 지정해주지 않거나type: ClusterIP
로 지정해주면 해당 리소스가 생성된다.
Cluster IP 실습
- 다음의 명령어 혹은 YAML 파일을 이용해 nginx Deployment 에 ClusterIP 서비스를 연결해준다.
kubectl expose deployment nginx --name nginx --port 80
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: ClusterIP
- 생성된 ClusterIP 를 확인한다.
kubectl get service nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP 10.105.103.161 <none> 80/TCP 4m2s
- ClusterIP 를 통해 접속이 이루어지는지 확인하기 위해 간단히 curl 이 설치되어있는 파드를 생성한다.
apiVersion: v1
kind: Pod
metadata:
name: curlpod
spec:
containers:
- name: curl
image: docker.io/curlimages/curl:7.86.0
command:
- sleep
- "infinity"
imagePullPolicy: IfNotPresent
restartPolicy: Always
- 다음의 명령어를 통해 curl 파드 내에서 통신을 시도해보고, NGINX 홈페이지 HTML 이 조회되는지 확인한다. (사용된 IP 는 다를 수 있다.)
# kubectl exec -it curlpod -- curl ${kubectl get service nginx 명령어를 통해 확인한 Internal IP 주소}
kubectl exec -it curlpod -- curl 10.105.103.161
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
- 하지만 ClusterIP 의 Internal IP 는 변경 가능성이 높기 때문에 파드간 통신에서는 Internal IP 를 직접적으로 사용하기 보다는 Domain 를 활용한다.
- 쿠버네티스에는 기본적으로 “CoreDNS” 라는 네임서버가 파드로 배포되어 있고, 클러스터 내부에서 사용되는 Domain 에 대한 처리를 담당한다.
- Domiain 은 다음과 같이 구성된다:
{서비스 이름}.{네임스페이스 이름}.svc.{클러스터 이름}.local
- 따라서 위에서 생성한 ClusterIP 에 대한 Domain 은 다음과 같다:
nginx.default.svc.cluster.local
- 참고)
{클러스터 이름}
은 기본값이cluster
이다. 하지만 클러스터 생성시에 다른 이름을 지정해줄 수도 있으니 정상적으로 작동하지 않는다면,kubectl exec -it 파드 -- cat /etc/resolv.conf
로 DNS 설정을 확인해보자.
- 참고)
- 다음 명령어를 통해 Internal IP 대신 Domain 으로 NGINX 에 접속해본다.
# kubectl exec -it curlpod -- curl ${ClusterIP 의 domain}
kubectl exec -it curlpod -- curl nginx.default.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
NodePort Service
NodePort 서비스란?
- NodePort 타입의 서비스는 모든 노드의 특정 30000-32767 사이의 포트를 열고, 해당 포트로 들어오는 트래픽을 파드로 전달해줄 수 있게 하는 리소스이다.
- 보통 클러스터 외부에서 파드에 접근하기 위해 사용한다.
NodePort 실습
- 다음의 명령어 혹은 YAML 파일을 통해 NodePort 서비스를 생성한다.
kubectl expose deployment nginx --name nginx-np --type NodePort --port 80
apiVersion: v1
kind: Service
metadata:
name: nginx-np
spec:
ports:
- name: http
nodePort: 31045
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: NodePort
- 생성한 NodePort 서비스를 확인한다.
kubectl get service nginx-np
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-np NodePort 10.110.201.171 <none> 80:31045/TCP 118s
- 참고) YAML 파일을 이용하면 개방할 30000 번대 포트를 직접 지정할 수 있지만, kubectl cli 를 이용하면 30000번대의 임의의 포트가 개방된다.
PORT(S)
항목에 바인드되어있는 30000 번대 포트를 통해 브라우저로 접속해본다.
LB Service
LB(LoadBalancer) 서비스란?
- 클러스터 외부의 로드밸런서와 연동하여 외부의 트래픽을 클러스터 내부의 파드로 전달하는 리소스
LB 서비스 vs NodePort 서비스
- LB 서비스와 NodePort 서비스 모두 외부의 트래픽을 파드에 전달한다는 점에서는 동일하다.
- 하지만, 두 서비스가 사용되는 Usecase 는 많이 다르다.
- LB 서비스의 경우에는 클러스터 외부의 로드밸런서 (SW 혹은 HW) 와 연동되어야 하기 때문에 온프레미스 클러스터에서는 사용하기 힘들다.
- 쿠버네티스 클러스터가 클라우드에서 작동하는 경우에는 Cloud controller 를 이용해 AWS 의 ALB 와 같은 리소스를 자동으로 생성하고,
- Docker desktop 혹은 minikube 의 경우에는 플러그인이 있어 자동적으로 처리해주지만 온프레미스 클러스터에서 안정적으로 사용할 수 있는 솔루션은 부족하다.
- LB 서비스의 경우에는 클러스터 외부의 로드밸런서 (SW 혹은 HW) 와 연동되어야 하기 때문에 온프레미스 클러스터에서는 사용하기 힘들다.
- 따라서 온프레미스 클러스터에서는 LB 서비스보다는 NodePort 를 이용해 외부의 트래픽을 전달받는다.
- 참고) NodePort 의 포트 범위가 30000-32767 이기에, 만일 80 이나 443 와 같은 Known port 로 트래픽을 받을 때에는 아래와 같이 앞단에 HAProxy 같은 프록시를 구성하기도 한다.
+-----------------------------+
+----> 32000/TCP(NodePort BE) K8s Node |
| +-----------------------------+
|
+--------------------+ | +-----------------------------+
--> 80/TCP(FE) HAPROXY |---+----> 32000/TCP(NodePort BE) K8s Node |
+--------------------+ | +-----------------------------+
|
| +-----------------------------+
+----> 32000/TCP(NodePort BE) K8s Node |
+-----------------------------+
Ingress
Ingress 란?
- ingress 는 L7 라우팅 규칙을 정의하는 리소스이다.
Ingress? Ingress controller?
- 쿠버네티스의 다른 리소스들과 마찬가지로, “리소스”, “리소스-컨트롤러” 와의 관계와 같다.
- “리소스”는 객체이고, 해당 객체에 명시된 내용에 따라 클러스터의 상태를 관리하는 프로세스(컨테이너) 가 “리소스-컨트롤러” 이듯이,
- Ingress 는 객체이고, 생성된 Ingress 에 따라 L7 라우팅 규칙을 업데이트하고 그에 따라 작동할 수 있게 하는 것이 Ingress controller 이다.
- NGINX 에 비유하자면, Ingress 는
nginx.conf
파일이고, Ingress controller 는 NGINX 프로세스이다.
- 쿠버네티스 내부에는 Ingress 리소스가 정의되어 있지만, 이것을 처리하는 컨트롤러 (Ingress controller)는 구현되어있지 않고 별도의 설치가 필요하다.
- 이것은 쿠버네티스가 직접 소프트웨어 L7 로드밸런서를 구현하기보다는 NGINX, HAProxy 같은 상용 솔루션들을 활용하고 사용자로 하여금 익숙한 솔루션을 선택할 수 있도록 한 것이다.
- 다만, 컨트롤러도 디플로이먼트의 형태로 구성되기에 이것을 외부에 노출시키기 위해 LB 서비스 혹은 NodePort 서비스의 도움이 필요하다.
Ingress 의 사용 이유
- NGINX, HAProxy 같은 일반적인 소프트웨어 L7 로드밸런서를 사용하는 이유와 같다.
- 즉, 클러스터로 진입하는 제일 앞단에서 TLS/SSL 인증서를 처리하거나, Application layer 에서의 라우팅을 이용해 클러스터 내부의 백엔드로 트래픽을 전달하는 용도로 사용한다.
- 따라서 원하는 파드를 외부에 노출시키고자 할 때 LB 혹은 NodePort 를 이용해 직접 노출시키는 것이 아닌 Ingress controller 만 외부에 노출시켜놓고 이것이 전달해주는 트래픽을 받을 수 있도록 Ingress 리소스를 생성해주면 된다.
Ingress 의 작동 원리
- Ingress controller 를 설치하고 외부에 노출한 뒤에는, Ingress 를 생성했을 때에 그에 맞는 L7 라우팅 규칙이 생성되어 외부의 트래픽이 파드로 전달되는 방식으로 작동한다.
- 트래픽을 규칙에 따라 파드로 전달할때에는, Ingress controller pod → target pod 간 통신, 즉, 클러스터 내부의 통신이기에 Cluster IP 타입의 서비스를 이용한다.
- 즉, L7 로드밸런서로 비유하자면, FE 는 Ingress controller 를 클러스터 외부에 노출시키는 LB type service 이고, BE 는 클러스터 내부의 파드에 연결된 Cluster IP type service 이며, 이 둘을 연결지어주는 규칙을 Ingress 리소스로 정의하게 되는 셈이다.
실습
- 먼저, Ingress 를 처리할 Ingress controller 를 설치한다.
- 아래의 명령어는 Kubernetes 에서 공식적으로 지원하는 Ingress controller 인 NGINX ingress controller 를
ingress-nginx
네임스페이스에 설치한다.
- 아래의 명령어는 Kubernetes 에서 공식적으로 지원하는 Ingress controller 인 NGINX ingress controller 를
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.5.1/deploy/static/provider/cloud/deploy.yaml
ingress-nginx
네임스페이스에 인그레스 컨트롤러가 정상적으로 설치되었는지 확인한다.
kubectl -n ingress-nginx get all
https://localhost
로 접속하여404: Not Found
화면이 보이는지 확인한다.404: Not Found
가 보이는 것은 Ingress controller 가 정상적으로 작동하고 있으나, root path 에 대한 ingress 가 생성되지 않았기에 자원을 찾을 수 없다는 문구를 보여주는 것이다.
- 신규 Ingress 를 생성한다.
- 본 Ingress 가 작동하기 위해서는
nginx
라는 이름의 ClusterIP 서비스와, 해당 서비스가 노출하고 있는 Deployment 등의 리소스가 필요하다. (앞선 실습에서 이미 생성되었다고 가정)
- 본 Ingress 가 작동하기 위해서는
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ing
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: localhost
http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: nginx
port:
number: 80
- 생성한 Ingress 에는 host 가
localhost
이고 path 가/testpath
일때 “nginx” 라는 이름의 서비스 오브젝트로 트래픽을 전달하도록 되어 있다. 따라서 브라우저에서https://localhost/testpath
로 접속해본다.
생성한 리소스 정리
kubectl delete deployment nginx
kubectl delete service nginx
kubectl delete service nginx-np
kubectl delete pod curlutils
kubectl delete ingress nginx-ing
kubectl delete -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.5.1/deploy/static/provider/cloud/deploy.yaml