HPA(Horizontal Pod Autoscaler)

# 간단하게 쓰고싶다면 스케일업이 유리, 보통은 스케일아웃 가장 많이 사용

  • 기존 상위 리소스까지 전부 삭제
kubectl delete deploy,rs,pod,svc --all
root@master:~# cd mani/
root@master:~/mani# mkdir hpa
root@master:~/mani# cd hpa/
  • 리소스를 제한할 파드 매니페스트
vi res.yml
apiVersion: v1
kind: Pod
metadata:
  name: live-pod
  labels:
    app: live-nginx
spec:
  containers:
  - name: nginx
    image: nginx:latest
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

# request: 최소자원, limits: 최대자원

 

pod(컨테이너)에 부여된 리소스에 임계값 정의를 통해, pod의 갯수를 늘리거나 줄이고 싶다.

오토스케일링을 위해선, 자원을 모니터링할 메트릭서버를 설치해야한다.

 

  • 메트릭서버가 있는 깃허브

https://github.com/kubernetes-sigs/metrics-server

  • 메트릭서버 매니페스트 다운로드
curl -Ls https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -o metric.yml

root@master:~/mani/hpa# vi metric.yml 

root@master:~/mani/hpa# kubectl apply -f metric.yml
root@master:~/mani/hpa# kubectl get pod -n kube-system

root@master:~/mani/hpa# kubectl apply -f res.yml
root@master:~/mani/hpa# kubectl top pod

# 메트릭서버가 설치되면 kubectl top pod 명령을 쓸 수 있다. 리소스의 자원 사용량을 조회 가능

  • 리소스를 좀 낮춰서 deploy & svc 생성
apiVersion: v1
kind: Service
metadata:
  name: svc-hpa
spec:
  selector:
    app: hpa-nginx
  ports:
  - port: 80
    targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hpa-dep
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hpa-nginx
  template:
    metadata:
      labels:
        app: hpa-nginx
    spec:
      containers:
      - image: 61.254.18.30:5000/nginx
        name: nginx-con
        resources:
          requests:  
            cpu: 10m
          limits:    
            cpu: 20m
kubectl apply -f hpa-dep.yml
  • cpu 사용량이 50% 이상일때, 스케일링을 할것이고, 최소1개-최대5개로 구성
    → kubectl autoscale deploy hpa-dep --min=1 --max=5 --cpu-percent=50
  • 위 명령을 매니페스트로 정의
vi as.yml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: hpa-nginx
spec:
  maxReplicas: 5
  minReplicas: 1
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: hpa-dep
  targetCPUUtilizationPercentage: 50
kubectl apply -f as.yml

  • 부하 주기
    → i=1; while true; do sleep 0.001; echo $((i++)) `curl -s <svc의 ClusterIP>`;done
i=1; while true; do sleep 0.001; echo $((i++)) `curl -s 10.101.141.129`;done
  • 다른 master에서 확인
watch kubectl top pod

kubectl get hpa

  • 트래픽을 취소하면 파드가 삭제되는걸 확인 가능
kubectl get pod --watch

  • 오토스케일러 삭제
kubectl delete hpa hpa-nginx

만약에 노드를 오토스케일링 하고 싶다면, AWS에는 카펜터(karpenter)를 통해 가능하다.


실습)

https://github.com/pcmin929/sb_code

이 스프링부트 앱을 여러분들의 쿠버네티스 클러스터에 배포하여 ilove.k8s.com 으로 접속했을때 앱이 뜨도록 해보세요. 또한 ilove.k8s.com/web 으로 접속했을때 readinessProbe를 통해 /healthz 경로로 헬스체크를 하는 간단한 httpd 웹서버를 띄워보세요. httpd 이미지는 61.254.18.30:5000/httpd로 올라가있는 상태, 위 두개의 앱은 오토스케일링 되어야한다.

  • 기존 상위 리소스까지 전부 삭제
kubectl delete deploy,rs,pod,svc,ns --all
root@master:~/mani# git clone https://github.com/pcmin929/sb_code.git
root@master:~/mani# cd sb_code
  • 이미지 pull
root@master:~/mani/sb_code# docker pull 61.254.18.30:5000/httpd
  • Dockerfile 작성
root@master:~/mani/sb_code# vi Dockerfile
FROM openjdk:8-jdk-alpine
COPY target/springbootApp.jar /app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
  • Maven Wrapper 실행
apt install -y maven
mvn clean package
  • 빌드
docker build -t leeseohoo/springboot-app .
docker push leeseohoo/springboot-app
  • 커스텀 httpd 이미지 생성
root@master:~/mani/sb_code# vi Dockerfile.httpd
FROM httpd
RUN echo "OK" > /usr/local/apache2/htdocs/healthz
docker build -t leeseohoo/httpd-healthz -f Dockerfile.httpd .
docker push leeseohoo/httpd-healthz
  • Deployment 및 Service YML

1. springboot 앱

root@master:~/mani/sb_code# vi springboot.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: springboot-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: springboot
  template:
    metadata:
      labels:
        app: springboot
    spec:
      containers:
        - name: springboot
          image: leeseohoo/springboot-app
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: springboot-service
spec:
  selector:
    app: springboot
  ports:
    - port: 80
      targetPort: 8080
root@master:~/mani/sb_code# kubectl apply -f springboot.yml 

 

2. httpd 웹서버 (/healthz 응답 포함)

root@master:~/mani/sb_code# vi httpd.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
        - name: httpd
          image: leeseohoo/httpd-healthz
          ports:
            - containerPort: 80
          readinessProbe:
            httpGet:
              path: /healthz
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: httpd-service
spec:
  selector:
    app: httpd
  ports:
    - port: 80
      targetPort: 80
root@master:~/mani/sb_code# kubectl apply -f httpd.yml 
  • Ingress YML
root@master:~/mani/sb_code# vi ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ilove-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2  # 정규표현식 기반 rewrite
spec:
  ingressClassName: nginx
  rules:
    - host: ilove.k8s.com
      http:
        paths:
          - path: /web(/|$)(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: httpd-service
                port:
                  number: 80
          - path: /
            pathType: Prefix
            backend:
              service:
                name: springboot-service
                port:
                  number: 80
root@master:~/mani/sb_code# kubectl apply -f ingress.yml
oot@master:~/mani/sb_code# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.12.1/deploy/static/provider/baremetal/deploy.yaml
root@master:~/mani/sb_code# vi deploy.yaml

root@master:~/mani/sb_code# kubectl apply -f deploy.yaml 

root@master:~/mani/sb_code# wget https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml

root@master:~/mani/sb_code#  kubectl apply -f metallb-native.yaml

root@master:~/mani/sb_code# vi config-metal.yml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 211.183.3.200-211.183.3.240 # 원하는 대역대, 안겹치게 수정
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system
root@master:~/mani/sb_code# kubectl apply -f config-metal.yml 
  • pod 확인
root@master:~/mani/sb_code# kubectl get pod -o wide

  • ingress-controller external-IP 확인
root@master:~/mani/sb_code# kubectl get svc -n ingress-nginx

root@master:~/mani/sb_code# vi /etc/hosts
# 클러스터 ingress IP
211.183.3.200 ilove.k8s.com
  • HPA
curl -Ls https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -o metric.yml
root@master:~/mani/hpa# vi metric.yml 

root@master:~/mani/hpa# kubectl apply -f metric.yml

cpu 사용량이 50% 이상일때, 스케일링을 할것이고, 최소1개-최대5개로 구성
→ kubectl autoscale deployment springboot-app --cpu-percent=50 --min=1 --max=5
→ kubectl autoscale deployment httpd-app --cpu-percent=50 --min=1 --max=5

  • 위 명령을 매니페스트로 정의

1. springboot-hpa

root@master:~/mani/sb_code# vi springboot-hpa.yml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: springboot-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: springboot-app
  minReplicas: 1
  maxReplicas: 5
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
root@master:~/mani/sb_code# kubectl apply -f springboot-hpa.yml

 

2. httpd-hpa

root@master:~/mani/sb_code# vi httpd-hpa.yml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: httpd-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: httpd-app
  minReplicas: 1
  maxReplicas: 5
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
root@master:~/mani/sb_code# kubectl apply -f httpd-hpa.yml
  • 결과 확인
curl ilove.k8s.com              # → Spring Boot 페이지
curl ilove.k8s.com/web          # → It works!
curl ilove.k8s.com/web/healthz  # → OK


  • HAproxy로 쓸 서버를 하나 clone

# ubun-tem을 하나 복제

  • IP를 211.183.3.50/24으로 설정
vi /etc/netplan/00-installer-config.yaml
netplan apply
  • 호스트네임을 haproxy로 변경
# 각각 vm 이름과 동일하게 호스트네임 설정
hostnamectl set-hostname haproxy
su
apt update -y
apt install -y haproxy
  • haproxy 설정파일 (/root에 백업)
cp /etc/haproxy/haproxy.cfg .
vi /etc/haproxy/haproxy.cfg
frontend kubernetes-master-lb
bind 0.0.0.0:6443
option tcplog
mode tcp

default_backend kubernetes-master-nodes

backend kubernetes-master-nodes
mode tcp
balance roundrobin
option tcp-check
option tcplog
server k8s-master1 211.183.3.101:6443 check
server k8s-master2 211.183.3.102:6443 check
server k8s-master3 211.183.3.103:6443 check

  • 설정 반영
systemctl restart haproxy
  • master와 worker1은 서스펜드 후, worker2만 종료

# 일단 worker2 노드를 한개 클론

  • 복제한 노드를 초기화
kubeadm reset --cri-socket unix:///run/containerd/containerd.sock
vi /etc/netplan/00-installer-config.yaml
netplan apply
hostnamectl set-hostname m1
  • nfs 마운트해제
vi /etc/fstab 

 

노드 구성 잘못한 경우, 초기화하고 싶은 경우

# kubeadm reset을 하게되면, 공통 설정은 다 들어있는 상태기 때문에 kubeadm init이 가능하다.

 

m1은 hostname m1에 IP는 211.183.3.101/24

m2은 hostname m2에 IP는 211.183.3.102/24

m3은 hostname m2에 IP는 211.183.3.103/24

 

m1

  • 이니셜라이징
kubeadm config images pull --cri-socket unix:///run/containerd/containerd.sock --kubernetes-version v1.30.3

# m1에서 이미지를 pull

kubeadm init --pod-network-cidr=10.244.0.0/16 --upload-certs --kubernetes-version=v1.30.2  --cri-socket unix:///run/containerd/containerd.sock --ignore-preflight-errors=all --control-plane-endpoint=211.183.3.50

# --control-plane-endpoint=211.183.3.50

# kubectl을 통해 api요청을 할 주소 = haproxy의 주소

m2, m3

  • m2, m3에서 마스터노드용 토큰 + --cri 옵션을 입력
kubeadm join 211.183.3.50:6443 --token 7bxl2u.7hhh8o4se1ar2gx8 \ --discovery-token-ca-cert-hash sha256:a6b74fb8d1dc94ad165cec275890ae564d7eaf6b9c22fb4eb6080a5a35a57e0e \ --control-plane --certificate-key a9471a19c262165d156eb33535225423308db5de627fd171c05b733adbcb9158 \ --cri-socket unix:///run/containerd/containerd.sock

haproxy 서버

스스로에게 kubectl명령을 치기 위해

kubectl 명령 설치 + 클러스터 m1의 config 파일 (/etc/kubernetes/admin.conf) 이 필요

  • kubectl 명령 설치
apt-get install -y apt-transport-https ca-certificates curl
mkdir -p /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
apt-get update
apt-get install -y kubectl
apt-mark hold kubectl
mkdir -p ~/.kube

m1의 파일내용을 haproxy에 복사

  • m1에서 config 파일의 내용을 복사
root@m1:~# cat /etc/kubernetes/admin.conf
  • haproxy 서버에서 config 파일을 생성하여 위 admin.conf의 내용을 복붙
root@haproxy:~# vi ~/.kube/config

♨ 만약에 join했는데도 Not Ready가 뜬다면

→ CNI 설치해 해결 가능

# kubectl 명령이 잘 되는것 확인

m1을 서스펜드 후 바로 haproxy 서버에서 kubectl get nodes를 치면

# 잠깐동안 안되다가 다시 명령이 들어가는걸 확인 가능

# 1분정도후에 m1을 서스펜드 했기때문에 NotReady 상태가 된다.

# control plan의 HA를 구성할땐, 반드시 control plan의 갯수를 홀수개로 가져가야 한다.