노드포트 추가하기
외부에서 쿠버네티스 클러스터의 내부에 접속하는 가장 쉬운 방법이다. 노드포트 서비스를 설정하면 모든 워커 노드의 특정 포트를 열고 여기로 오는 모든 요청을 노드포트 서비스로 전달한다.
= 노드포트 서비스 구성도
= nodeport.yaml
apiVersion: v1
kind: Service
# 서비스의 이름
metadata:
name: np-svc
spec:
# 셀렉터의 레이블 지정
selector:
app: np-pods
# 사용할 프로토콜과 포트들을 지정
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
nodePort: 30000
# 서비스 타입을 설정
type: NodePort
- kind를 Service로 변경
- spec에 ports정보가 추가
# pod생성
[root@m-k8s-ys vagrant]# kubectl create deployment np-pods --image=sysnet4admin/echo-hname
# 노드포트 타입의 서비스 생성
[root@m-k8s-ys vagrant]# kubectl create -f ./nodeport.yaml
# 노드포트 서비스로 생성한 서비스 확인
[root@m-k8s-ys vagrant]# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d1h
np-svc NodePort 10.104.205.195 <none> 80:30000/TCP 25s
# 쿠버네티스 클러스터의 워커 노드 IP 확인
[root@m-k8s-ys vagrant]# kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
m-k8s-ys Ready master 7d1h v1.18.4 192.168.56.10 <none> CentOS Linux 7 (Core) 3.10.0-1127.19.1.el7.x86_64 docker://1.13.1
w1-k8s-ys Ready <none> 7d1h v1.18.4 192.168.56.101 <none> CentOS Linux 7 (Core) 3.10.0-1127.19.1.el7.x86_64 docker://1.13.1
w2-k8s-ys Ready <none> 7d1h v1.18.4 192.168.56.102 <none> CentOS Linux 7 (Core) 3.10.0-1127.19.1.el7.x86_64 docker://1.13.1
w3-k8s-ys Ready <none> 7d1h v1.18.4 192.168.56.103 <none> CentOS Linux 7 (Core) 3.10.0-1127.19.1.el7.x86_64 docker://1.13.1
# http://192.168.56.101:30000 에 노드 접근가능
# expose 명령어를 통해 서비스로 내보낼 디플로이먼트를 np-pods로 지정
[root@m-k8s-ys vagrant]# kubectl expose deployment np-pods --type=NodePort --name=np-svc-v2 --port=80
service/np-svc-v2 exposed
# 서비스 확인
[root@m-k8s-ys vagrant]# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d1h
np-svc NodePort 10.104.205.195 <none> 80:30000/TCP 12m
np-svc-v2 NodePort 10.104.102.252 <none> 80:32336/TCP 12s
# http://192.168.56.101:32336 접근가능
- 부하분산도 자동으로 된다고 한다.
인그레스(Ingrass)
노드포트 서비스는 포트를 중복 사용이 불가능하여 1개의 노드포트에 1개의 deployment만 적용된다. 그럼 여러개의 deployment가 있다면 포트가 여러개를 사용한다는 소리인데, 이때 인그레스(Ingrass)를 사용하여 포트를 여러개를 사용하지 않고 하나의 포트만 사용하여 처리할 수 있다.
인그레스는, 클러스터 외부에서 내부 서비스로 HTTP와 HTTPS 경로를 노출한다. 트래픽 라우팅은 인그레스 리소스에 정의된 규칙에의해 컨트롤된다. 그리고 외부에서 서비스로 접속이 가능한 URL, 로드 밸런서 트래픽, SSL(Secure Sockets Layer) / TLS(Transport Layer Security) 종료 그리고 이름-기반의 가상 호스팅을 제공하도록 구성할 수 있다. (= OSI 7계층의 L7 로드밸런싱 작업을 할 수 있다)
인그레스는 임의의 포트 또는 프로토콜을 노출시키지 않으며 HTTP와 HTTPS 이외의 서비스를 인터넷에 노출하려면 위에서 설명한 노드포트 혹은 로드밸런서를 사용해야 한다. 인그레스 자체는 이런 규칙들을 정의해둔 자원이고 이런 규칙들을 실제로 동작하게 해주는게 인그레스 컨트롤러(ingress controller)이다.
예를들어 인그레스를 통과하여 어플리케이션 계층에서 HTTP(s) 기반의 URL Path에 따라 각각 다른 서비스로 라우팅해주는 구조를 가질 수 있다.
규칙들을 정의한 인그레스를 동작하게 해주는 인그레스 컨트롤러는 다양하다. 어떤 것들이 있는지는 k8s의 공식사이트 참조.
(https://kubernetes.io/ko/docs/concepts/services-networking/ingress-controllers/)
본인은 서적에 나와있는 Nginx 인그레스 컨트롤러를 통해 블로그 포스팅을 진행할 것이다.
1] 사용자는 노드마다 설정된 노드포트를 통해 노드포트 서비스로 접속하고, 노드포트 서비스를 Nginx 인그레스 컨트롤러로 구성
# 테스트용 deployment - in-hname-pod 배포
[root@m-k8s-ys vagrant]# kubectl create deployment in-hname-pod --image=sysnet4admin/echo-hname
deployment.apps/in-hname-pod created
# 테스트용 deployment - in-ip-pod 배포
[root@m-k8s-ys vagrant]# kubectl create deployment in-ip-pod --image=sysnet4admin/echo-ip
deployment.apps/in-ip-pod created
# 배포된 파드 상태를 확인
[root@m-k8s-ys vagrant]# kubectl get pods
NAME READY STATUS RESTARTS AGE
in-hname-pod-8565c86448-2tp29 1/1 Running 0 34s
in-ip-pod-76bf6989d-sjnbw 1/1 Running 0 10s
# Nginx 인그레스 컨트롤러 설치
[root@m-k8s-ys vagrant]# kubectl apply -f ingress-nginx.yaml
namespace/ingress-nginx created
configmap/nginx-configuration created
configmap/tcp-services created
configmap/udp-services created
serviceaccount/nginx-ingress-serviceaccount created
clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created
role.rbac.authorization.k8s.io/nginx-ingress-role created
rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created
deployment.apps/nginx-ingress-controller created
limitrange/ingress-nginx created
= ingress-nginx.yaml예제 참고: https://github.com/sysnet4admin/_Book_k8sInfra/blob/main/ch3/3.3.2/ingress-nginx.yaml
2] Nginx 인그레스 컨트롤러는 사용자의 접속 경로에 따라 적합한 클러스터 IP 서비스로 경로를 제공
= ingress-config.yaml
: 들어오는 주소 값과 포트에 따라 노출될 서비스를 연결하는 역할을 설정. 외부에서 주소 값과 노드포트를 가지고 들어오는 것은 hname-svc-default 서비스와 연결된 파드로 접속 하고, URL path 뒤에 각각 [/ip, /your-directory] 를 추가한 주소 값은 각각 [ip-svc / your-svc] 서비스와 연결된 파드로 접속
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-nginx # Ingress의 이름 (이름을 통해서 통신할 ingress 컨트롤러를 확인)
annotations: # 메타데이터의 기록 및 변경
# (여기선 rewrite-target을 "/"(기본주소)로 지정)
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules: # 규칙
- http:
paths:
- path: # 기본 경로 규칙
backend: # 연결되는 서비스와 포트
serviceName: hname-svc-default
servicePort: 80
- path: /ip
backend:
serviceName: ip-svc
servicePort: 80
- path: /your-directory
backend:
serviceName: your-svc
servicePort: 80
# Nginx 인그레스 컨트롤러 파드 배포확인
# (-n 옵션은 namespace의 약어로, default 네임스페이스가 아닌 ingress-nginx 네임스페이스에 속하기 때문에 아래와 같은 옵션을 부여해야함)
[root@m-k8s-ys vagrant]# kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
nginx-ingress-controller-5bb8fb4bb6-595dr 1/1 Running 0 7m33s
# ingress-config.yaml 적용
[root@m-k8s-ys vagrant]# kubectl apply -f ingress-config.yaml
ingress.networking.k8s.io/ingress-nginx created
# ingress 설정 파일 적용 확인
[root@m-k8s-ys vagrant]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress-nginx <none> * 80 7m12s
# 인그레스에 요청한 내용 반영내용 확인 (yaml 형식)
[root@m-k8s-ys vagrant]# kubectl get ingress -o yaml
3] 클러스터 IP 서비스는 사용자를 해당 파드로 연결
= ingress.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-ingress-controller # 서비스 이름
namespace: ingress-nginx # 네임스페이스 이름
spec:
# 사용할 프로토콜과 포트들을 지정
ports:
# http에 대한 프로토콜 및 포트 지정
- name: http
protocol: TCP
port: 80
targetPort: 80
nodePort: 30100
# https에 대한 프로토콜 및 포트 지정
- name: https
protocol: TCP
port: 443
targetPort: 443
nodePort: 30101
selector: # 셀렉터의 레이블 지정
app.kubernetes.io/name: ingress-nginx
# 서비스 타입을 설정
type: NodePort
# ingress.yaml 적용 (ports -> TCP)
[root@m-k8s-ys vagrant]# kubectl apply -f ingress.yaml
service/nginx-ingress-controller created
# 노드포트 서비스로 생성된 nginx 인그레스 컨트롤러 확인
[root@m-k8s-ys vagrant]# kubectl get services -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-ingress-controller NodePort 10.97.23.203 <none> 80:30100/TCP,443:30101/TCP 8m57s
# expose 명령으로 deployment(in-hname-pod, in-ip-pod)를 서비스로 노출
[root@m-k8s-ys vagrant]# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 35d
[root@m-k8s-ys vagrant]# kubectl expose deployment in-hname-pod --name=hname-svc-default --port=80,443
service/hname-svc-default exposed
[root@m-k8s-ys vagrant]# kubectl expose deployment in-ip-pod --name=ip-svc --port=80,443
service/ip-svc exposed
[root@m-k8s-ys vagrant]# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hname-ip-svc ClusterIP 10.97.205.97 <none> 80/TCP,443/TCP 4s
hname-svc-default ClusterIP 10.99.202.194 <none> 80/TCP,443/TCP 29s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 35d
노드포트 세팅후 http(30100), https(30101) 결과 확인
= http - default
= http - /ip
= https - default
= https - /ip
테스트했던 deployment와 파드들은 깔끔하게 삭제해주자.
[root@m-k8s-ys vagrant]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
in-hname-pod 1/1 1 1 59m
in-ip-pod 1/1 1 1 59m
[root@m-k8s-ys vagrant]# kubectl delete deployment in-hname-pod
deployment.apps "in-hname-pod" deleted
[root@m-k8s-ys vagrant]# kubectl delete deployment in-ip-pod
deployment.apps "in-ip-pod" deleted
[root@m-k8s-ys vagrant]# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hname-svc-default ClusterIP 10.99.202.194 <none> 80/TCP,443/TCP 18m
ip-svc ClusterIP 10.105.105.253 <none> 80/TCP,443/TCP 5m1s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 35d
[root@m-k8s-ys vagrant]# kubectl delete services hname-svc-default
service "hname-svc-default" deleted
[root@m-k8s-ys vagrant]# kubectl delete services ip-svc
service "ip-svc" deleted
지금까지 했던 내용들은 들어오는 요청을 모두 워커 노드의 노드포트를 통해 노드포트 서비스로 이동하고, 이를 다시 쿠버네티스의 파드로 보내는 구조였는데, 이 방식은 매우 비효율적이기 때문에.. 아래에서 정리할 로드밸런서 서비스 타입을 제공해 파드를 외부에 노출하고 부하를 분산한다.
로드밸런서(Load Balancer)
EKS, GKE, AKS 등 클라우드에서는 쉽게 쿠버네티스 클러스터에 로드밸런서 서비스를 생성하여 외부와 통신할 수 있는 IP가 부여되고 외부와 통신을 할 수 있으며 부하분산도 이루어진다. 그렇다면 반대로 실습을 진행하는 onPromise 환경에서는 로드밸런스를 어떻게 구성할 수 있을까?
MetalLB를 통해 구현할 수 있다. MetalLB는 특별한 네트워크 설정이나 구성이 있는 것이 아니고, 기존의 L2 네트워크와 L3 네트워크로 로드밸런서를 구현한다.
MetalLB 컨트롤러는 작동 방식(Protocol)을 정의하고 EXTERNAL-IP를 부여해 관리한다. MetalLB Speaker는 정해진 작동 방식(L2/ARP, L3/BGP)에 따라 경로를 만들 수 있도록 네트워크 정보를 광고하고 수집해 각 파드의 경로를 제공한다. 이때 L2는 Speaker중에서 리더를 선출해 경로 제공을 총괄한다.
# 2종류의 deployment를 만들고 파드를 3개로 늘림
[root@m-k8s-ys vagrant]# kubectl create deployment lb-hname-pods --image=sysnet4admin/echo-hname
deployment.apps/lb-hname-pods created
[root@m-k8s-ys vagrant]# kubectl scale deployment lb-hname-pods --replicas=3
deployment.apps/lb-hname-pods scaled
[root@m-k8s-ys vagrant]# kubectl create deployment lb-ip-pods --image=sysnet4admin/echo-ip
deployment.apps/lb-ip-pods created
[root@m-k8s-ys vagrant]# kubectl scale deployment lb-ip-pods --replicas=3
deployment.apps/lb-ip-pods scaled
# 파드수 확인 (2*3)
[root@m-k8s-ys vagrant]# kubectl get pods
NAME READY STATUS RESTARTS AGE
lb-hname-pods-79b95c7c7b-7gqwk 1/1 Running 0 99s
lb-hname-pods-79b95c7c7b-rftzw 1/1 Running 0 79s
lb-hname-pods-79b95c7c7b-x4jtl 1/1 Running 0 79s
lb-ip-pods-6c6bb59b4-55jv7 1/1 Running 0 53s
lb-ip-pods-6c6bb59b4-7777k 1/1 Running 0 63s
lb-ip-pods-6c6bb59b4-czt7l 1/1 Running 0 53s
= metallb.yaml예제 참고: https://github.com/sysnet4admin/_Book_k8sInfra/blob/main/ch3/3.3.4/metallb.yaml
# metallb.yaml 배포
[root@m-k8s-ys vagrant]# kubectl apply -f metallb.yaml
namespace/metallb-system created
podsecuritypolicy.policy/speaker created
serviceaccount/controller created
serviceaccount/speaker created
clusterrole.rbac.authorization.k8s.io/metallb-system:controller created
clusterrole.rbac.authorization.k8s.io/metallb-system:speaker created
role.rbac.authorization.k8s.io/config-watcher created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker created
rolebinding.rbac.authorization.k8s.io/config-watcher created
daemonset.apps/speaker created
deployment.apps/controller created
# 배포된 MetalLB의 파드 5개 확인
[root@m-k8s-ys vagrant]# kubectl get pods -n metallb-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
controller-5f98465b6b-j4q5p 1/1 Running 0 41s 172.16.138.81 w3-k8s-ys <none> <none>
speaker-6z9q7 1/1 Running 0 41s 192.168.56.101 w1-k8s-ys <none> <none>
speaker-k9vwj 1/1 Running 0 41s 192.168.56.103 w3-k8s-ys <none> <none>
speaker-v2rh7 1/1 Running 0 41s 192.168.56.10 m-k8s-ys <none> <none>
speaker-w8wgp 1/1 Running 0 41s 192.168.56.102 w2-k8s-ys <none> <none>
# MetalLB 설정 배포 (metallb-l2config.yaml)
= metallb-l2config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system # 네임스페이스 이름
name: config # 컨피그맵 이름
data:
# 설정 내용
config: |
# metallb의 세부 설정
address-pools:
- name: nginx-ip-range
protocol: layer2 # metallb에서 제공하는 로드밸런서의 동작 방식
addresses: # metallb에서 제공하는 로드밸런서의 Ext 주소
- 192.168.56.11-192.168.56.13
# metallb 설정 배포
[root@m-k8s-ys vagrant]# kubectl apply -f metallb-l2config.yaml
configmap/config created
# ConfigMap 생성 확인
[root@m-k8s-ys vagrant]# kubectl get configmap -n metallb-system
NAME DATA AGE
config 1 28s
# MetalLb 설정 확인
[root@m-k8s-ys vagrant]# kubectl get configmap -n metallb-system -o yaml
# lb-hname-pods, lb-ip-pods 파드들을 로드밸런서 서비스로 노출
[root@m-k8s-ys vagrant]# kubectl expose deployment lb-hname-pods --type=LoadBalancer --name=lb-hname-svc --port=80
service/lb-hname-svc exposed
[root@m-k8s-ys vagrant]# kubectl expose deployment lb-ip-pods --type=LoadBalancer --name=lb-ip-svc --port=80
service/lb-ip-svc exposed
# 로드밸런서 서비스 확인
[root@m-k8s-ys vagrant]# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 35d
lb-hname-svc LoadBalancer 10.110.235.10 192.168.56.11 80:32091/TCP 79s
lb-ip-svc LoadBalancer 10.99.167.44 192.168.56.12 80:32763/TCP 14s
(!) 여기서 scale의 -replicas=6 (리플리카수 증가)를 하게되면, 추가된 pod에도 접근이 바로 된다. (개꿀)
MetalLB 세팅후 결과 확인
부하분산이 정상적으로 잘 이루어지고 있는 모습
역시나 테스트했던 deployment와 파드들은 깔끔하게 삭제해주자.
[root@m-k8s-ys vagrant]# kubectl delete deployment lb-hname-pods
deployment.apps "lb-hname-pods" deleted
[root@m-k8s-ys vagrant]# kubectl delete deployment lb-ip-pods
deployment.apps "lb-ip-pods" deleted
[root@m-k8s-ys vagrant]# kubectl delete service lb-hname-svc
service "lb-hname-svc" deleted
[root@m-k8s-ys vagrant]# kubectl delete service lb-ip-svc
service "lb-ip-svc" deleted
부하에 따라 자동으로 파드 수를 조절하는 HPA
HPA(Horizontal Pod Autoscaler)란? 트래픽 증가로 파드가 더 이상 감당할 수 없어, 서비스 불가상태에 도달했을 때 부하량에 따라 deployment의 pods 수를 유동적으로 관리하는 기능
HPA가 자원을 요청할 때 Metrics-Server를 통해 계측값을 전달받는다. 파드수를 조절하는 HPA를 사용하기 위해서는 Metrics-Server를 설정해야 한다.
(!)Pod를 생성하고 LoadBalancer를 생성하여 expose시키는 과정은 위 설정과 동일하다.
# 부하를 확인하는 k8s 명령어. (아래 오류는 메트릭 서버가 없어서 나옴)
[root@m-k8s-ys vagrant]# kubectl top pods
Error from server (NotFound): the server could not find the requested resource (get services http:heapster:)
# 정상적으로 나옴 (현재는 파드수가 1개. 아래 autoscale을 통해 실제로 부하가 발생시 파드수가 늘어난다)
[root@m-k8s-ys vagrant]# kubectl top pods
NAME CPU(cores) MEMORY(bytes)
hpa-hname-pods-75f874d48c-2z7gj 0m 1Mi
= metallb.yaml예제 참고: https://github.com/sysnet4admin/_Book_k8sInfra/blob/main/ch3/3.3.5/metrics-server.yaml
# 배포된 deployment 설정을 수정
[root@m-k8s-ys vagrant]# kubectl edit deployment hpa-hname-pods
# pod 오토스케일링 설정 (파드수 최소 1개, 최대 30개, CPU 사용률 50%인경우 증가)
[root@m-k8s-ys vagrant]# kubectl autoscale deployment hpa-hname-pods --min=1 --max=30 --cpu-percent=50
horizontalpodautoscaler.autoscaling/hpa-hname-pods autoscaled
# HPA의 현재 상태를 요약해서 보여줌
[root@m-k8s-ys vagrant]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-hname-pods Deployment/hpa-hname-pods <unknown>/50% 1 30 1 2m7s
참고자료
= 서적 - 컨테이너 인프라 환경 구축을 위한 쿠버네티스/도커, 길벗 - 제 3장
= https://kubernetes.io/ko/docs/concepts/services-networking/ingress/
= https://bcho.tistory.com/1263
'Docker & Kubernetes' 카테고리의 다른 글
Docker - DockerFile (0) | 2022.04.05 |
---|---|
쿠버네티스(Kubernetes) - 오브젝트 (0) | 2022.03.27 |
쿠버네티스(Kubernetes) - 베이그런트 (Vagrant) (0) | 2022.01.29 |
쿠버네티스(Kubernetes) - 기본적인 사용방법 (0) | 2022.01.29 |
쿠버네티스(Kubernetes) - 이해하기 (0) | 2022.01.22 |