데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      AKS 상용 서비스 적용기 (3) - 서비스를 견고하게 해주는 k8s 추가 설정 (HPA, PV/PVC, Probe, preStop)

      dragonpipe 24.08.01
      1,671 7 0
      DEVOTEE 요약
      본 블로그에서는 서비스 안정성과 관련된 Kubernetes 리소스 설정에 대해 설명합니다. 첫째, 오토스케일링을 위한 HPA를 설정하고 테스트한 결과를 공유합니다. 둘째, 파일 저장을 위한 Volume 설정과 이를 deployment에 적용하는 방법을 설명합니다. 셋째, Probe 설정을 통해 서비스를 안정화하는 방법과 성능 영향을 테스트합니다.
      DEVOTEE 추천 블로그

      안녕하세요, dragonpipe입니다.

      이 번에는 지난 게시글에 이어 AKS(Azure Kubernetes Service) 상용 서비스에 적용한 후기 세 번째 내용으로,

      서비스 안정성에 도움이 되는 k8s 리소스의 manifest yaml 파일에 대해 소개드리려고 합니다.


      1. HPA 설정 : AutoScaling을 위한 오브젝트

      k8s의 큰 장점 중 하나가 오토스케일링 기능이라고 생각하는데요. HPA 리소스가 있으면 auto-scale 기능을 어렵지 않게 구현할 수 있습니다.

      HPA 리소스를 생성하기 위한 manifest 파일을 어떻게 작성하였는지 공유 드리고, 추가로 HPA가 잘 동작하는지 테스트 해 본 결과를 공유드리겠습니다.

      1. hpa.yaml 파일 작성

        • kind : HorizontalPodAutoscaler 추가

        • minReplicas : 최소 replica 개수 (hpa의 minReplicas와 deployment의 replica 값을 맞추는 것을 권장)

        • maxReplicas : 최대 replica 개수

        • metrics 설정

          • Memory 사용량 기준 autoscaling 설정

            • name : memory

            • target.type : Utilization

            • target.averageUtilization : 기준 사용량(평균)

          • CPU 사용량 기준 autoscaling 설정

            • name : cpu

            • target.type : Utilization

            • target.averageUtilization : 기준 사용량(평균)

        apiVersion: autoscaling/v2
        kind: HorizontalPodAutoscaler
        metadata:
          name: inspect-dna-api
        spec:
          scaleTargetRef:
            apiVersion: apps/v1
            kind: Deployment
            name: inspect-dna-api
          minReplicas: 1
          maxReplicas: 3
          metrics:
          - type: Resource
            resource:
              name: memory
              target:
                type: Utilization
                averageUtilization: 90
          - type: Resource
            resource:
              name: cpu
              target:
                type: Utilization
                averageUtilization: 80
      2. jmeter로 부하를 주어 HPA가 자동으로 오토스케일링 하는지 확인

        • CPU와 Memory에 부하를 주는 코드 작성

          hpa(1).png

      • jmeter로 부하 테스트 수행

        hpa(2).png

        • kubectl 명령어를 통해 HPA 상태와 replica 숫자 확인

          1. replica : 1일 때 Memory가 제한 기준 90%를 상회

            hpa(3).png

          2. replica : 2로 증가하였지만 CPU 제한 기준 80%를 상회

            hpa(4).png

          3. replica: 3으로 증가하였지만 Memory, CPU 모두 제한 기준을 상회 → 하지만 maxReplicas가 3이므로 더 이상 replica는 증가하지 않음

            hpa(5).png


      2. Volume 설정 : 파일 저장 및 관리를 위한 스토리지 서비스 (외부 스토리지 연결)

      k8s에서 pod는 언제든지 삭제되고 재생성 될 수 있습니다.

      만약 파일을 업로드 하였을 때 local 서버에만 저장한다면 삭제 후 재생성되면 파일도 함께 삭제될 수 있습니다.

      그렇기 때문에 영구적인 파일 저장 스토리지가 필요한데 이를 가능하게 해주는 리소스가 volume입니다.

      pod에서 사용하려면 blob storage와 같은 저장소를 PV와 PVC로 프로비저닝을 해야 하는데, 해당 내용은 별도로 공유드리겠습니다.

      우선 이번 게시글에서는 pv와 pvc가 생성되었다고 가정하고 deployment에서 어떻게 pvc를 적용할 수 있는지에 대해 설명드리겠습니다.

      1. deployment.yaml에 pvc 설정 추가

        • volume을 mount하기 위해선 미리 PV와 PVC가 생성되어야 함 (PV와 PVC 생성 가이드는 추후 공유 예정)

        • deployment.yaml 파일에 미리 생성한 pvc를 volume으로 mount하는 부분 추가

        • volumeMounts : containers 밑에 생성

          • name : 바깥 volumes의 name 중 일치하는 volume을 mount 하겠다는 의미로 volumes의 name과 일치하면 되고 자율적으로 네이밍 가능

          • mountPath : 컨테이너에서 mount 할 경로

        • volumes : containers와 같은 레벨에 생성

          • name : volumeMounts의 name과 일치해야 함

          • persistentVolumeClaim.claimName : pvc name 추가

        ...
          volumeMounts:
          - name: azurefile
            mountPath: /mnt/oavatarfiles
        volumes:
        - name: azurefile
          persistentVolumeCl
            claimName: azurefile-pvc
        ...


      3. Probe 설정 : Kubernetes Pod의 상태 진단을 담당하는 서비스 (안정성과 항상성 향상)

      제가 운영하고 있는 서비스는 아직 Rolling update 방식으로 배포하고 있습니다. 시간이 되면 Blue/Green 방식으로 배포하는 것도 진행해보려고 합니다.

      Rolling update 방식으로 배포할 때는 반드시 Probe 설정을 해주어야 하는데요.

      왜냐하면 배포할 때 하나 또는 여러개씩 pod가 생성되는 데 이 때 실제 컨테이너 내부 애플리케이션이 부팅 완료되기도 전에

      k8s에서 해당 서비스가 살아있다고 인지하면 네트웍 트래픽을 전달할 수도 있기 때문입니다.

      따라서 애플리케이션 부팅이 완료되어 서비스를 정상적으로 수행할 수 있는 상태인지 k8s에서 인지할 수 있게 Probe 설정을 해주어야 합니다.

      이번에는 Probe에는 어떤 것들이 있고 어떻게 설정하는지에 대해 알아보겠습니다.

      1. Probe 종류

        • livenessProbe : 컨테이너가 동작 중인지 여부를 나타냄. 활성 프로브(liveness probe)에 실패한다면, kubelet은 컨테이너를 죽이고, 해당 컨테이너는 재시작 정책의 대상이 됨.

          만약 컨테이너가 활성 프로브를 제공하지 않는 경우, 기본 상태는 Success 이다.

        • readinessProbe : 컨테이너가 요청을 처리할 준비가 되었는지 여부를 나타냄.

          준비성 프로브(readiness probe)가 실패한다면, 엔드포인트 컨트롤러는 파드에 연관된 모든 서비스들의 엔드포인트에서 파드의 IP 주소를 제거.

          준비성 프로브의 초기 지연 이전의 기본 상태는 Failure. 만약 컨테이너가 준비성 프로브를 지원하지 않는다면, 기본 상태는 Success 이다.

        • startupProbe : 컨테이너 내의 애플리케이션이 시작되었는지를 나타냄.

          스타트업 프로브(startup probe)가 주어진 경우, 성공할 때 까지 다른 나머지 프로브는 활성화 되지 않음.

          만약 스타트업 프로브가 실패하면, kubelet이 컨테이너를 죽이고, 컨테이너는 재시작 정책에 따라 처리. 컨테이너에 스타트업 프로브가 없는 경우, 기본 상태는 Success 이다.

      2. 테스트 수행

        • 비기능(성능) 테스트 결과 replica 개수가 2개 이상인 상황에서 readinessProbe을 설정하였을 경우 안정성 향상

        • jmeter 테스트 결과 readinessProbe 설정 없을 때 대비 Error 발생율이 10% 이하로 떨어짐 (12.3% → 1.1%)

          • 사유 : 한 pod에서 문제가 발생하였을 때 해당 pod가 정상으로 돌아올 때 까지 다른 pod로 네트웍 트래픽을 고정해주므로 에러 발생율 낮아짐

          • 단점 : 성능(throughput)이 다소 떨어짐 (5.5/sec → 5.1/sec)

        • HPA를 설정한 상태에서 startupProbe를 설정하였을 경우 안정성 향상

        • HPA를 설정할 경우 리소스 자원이 설정한 기준치 보다 상회할 경우 replica 개수를 늘리므로 pod가 중간에 생성될 수 있음

        • 생성되는 중간에 (구동이 완료되지 않은 상태에서) ingress를 통해 요청이 들어오면 에러가 발생할 수 있음

        • startupProbe를 설정하면 컨테이너가 정상적으로 다 구동될 때 까지 트래픽을 전달하지 않으므로 안정성 향상

      3. deployment.yaml 파일에서 Probe설정(readinessProbe, startupProbe) 추가 (containers 하위에 추가)

        • readinessProbe 설정

          • path : spring actuator 사용 시 "/actuator/health/readiness"로 고정

            • 해당 경로로 테스트로 호출해 보면 json 결과 값 확인 가능 : {"status": "UP"}

          • initialDelaySeconds : 시작 후 readiness 상태를 확인할 때 까지 기다리는 시간 (어플리케이션 구동에 걸리는 평균 시간을 입력할 것)

          • periodSeconds : readiness 상태를 확인하는 주기

        • startupProbe 설정

          • path : spring actuator 사용 시 "/actuator/health/liveness"로 고정 (Spring Actuator에 startupProbe을 위한 API 없지만 liveness API로 대체 가능)

            • 해당 경로로 테스트로 호출해 보면 json 결과 값 확인 가능 : {"status": "UP"}

          • initialDelaySeconds : 시작 후 readiness 상태를 확인할 때 까지 기다리는 시간 (어플리케이션 구동에 걸리는 평균 시간을 입력할 것)

          • periodSeconds : readiness 상태를 확인하는 주기

        ...
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 80
            scheme: HTTP
          initialDelaySeconds: 30
          periodSeconds: 5
        startupProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 80
            scheme: HTTP
          initialDelaySeconds: 30
          periodSeconds: 5
        ...
      4. Spring-boot project에서 health check를 위한 actuator 기능 추가

        • pom.xml: 애플리케이션에 readiness 체크를 위한 설정 추가 (Spring Actuator 사용)

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        • application.yaml: health.probe.enabled: true로 설정

        management:
          endpoint:
            health:
              probes:
                enabled: true


      4. Graceful Shutdown을 위한 preStop 설정

      • Graceful Shutdown이란?

        : 현재 들어온 요청을 모두 수행하고 우아하게 종료된다는 개념으로, 수행 중이던 요청을 미처 처리하지 못하고 종료되면 리소스가 서버에 좀비처럼 남아있거나 데이터 손실을 일으킬 수 있는 경우 필요

      Probe 설정이 Pod가 생성될 때의 안정성을 보장해 준다면, preStop 설정은 Pod가 종료될 때의 안정성을 보장해줄 수 있습니다.

      Pod가 다른 리소스와 커넥션이 되어 있을 때 Pod를 강제 종료하게 되면 Pod는 shutdown 되지만 연결되었던 리소스는 진행하던 프로세스를 계속 진행할 수 있습니다.

      프로세스 이후 무언가 요청을 주었던 pod에 응답을 준다거나 인터렉션을 하는 과정에서 에러가 발생할 수 있으므로 우아하게(?) 종료될 수 있도록 preStop 설정을 추가하였습니다.

      1. preStop으로 graceful shutdown을 수행해야 하는 이유

        • kube-proxy나 Ingress controller 등은 엔드포인트 변경에 대해 알림을 받기까지 다소 시간이 걸림

        • 따라서 트래픽은 종료 된것으로 표시가 되어도 여전히 포드(Pod)로 트래픽이 흐를 수 있음

        • 새로운 요청 수락을 중지하고 모든 연결이 종료되면 종료되어야 할 때 'lifecycle.preStop' 설정 고려

      2. deployment.yaml 파일에 설정 추가

        • preStop 설정 (containers 하위에 추가)

          • lifecycle.preStop.exec.command에 확인이 필요한 사항을 sh로 작성 (예 : "while netstat -at -n -nn|grep 10000|grep ESTABLISHED; do sleep 1; done")

          • 예시 상황: grpc 커넥션 맺고 있는 상황에서 pod가 삭제될 경우 커넥션이 끊어져서 target 서버에서 rpc 통신 할 때 에러 발생

          • 조치 예시: 포트가 10000번 이므로 1초에 한번씩 10000번 포트로 ESTABLISHED 상태인지 확인 하고 없으면 done 처리

          • 테스트 결과: 커넥션 맺고 있는 동안은 컨테이너가 종료되지 않고 모든 커넥션이 종료되면 컨테이너가 종료되고 pod가 삭제됨을 확인

        • terminationGracePeriodSeconds 설정 (containers와 동일 레벨에 추가)

          • 초 단위로 몇 초까지 종료 조건을 확인할지 설정

          • 만약 60이라고 설정하면 60초가 넘어가면 preStop 조건이 충족하지 않아도 종료 처리

        ...
        terminationGracePeriodSeconds: 60
        containers:
        - name: {app명}
          image: {acr domain url}/{app명}/{env}/{image명}:0.1.150
          lifecycle:
            preStop:
              exec:
                command: ["/bin/sh","-c","while netstat -at -n -nn|grep 10000|grep ESTABLISHED; do sleep 1; done"]
        ...


      마치는 글

      이 번엔 서비스의 안정성을 높여주는 k8s 리소스 설정에 대해 알아보았습니다.

      개인적으론 HPA의 metrics설정은 서비스 특성에 맞게 조심스럽게 설정해야 된다고 느꼈습니다.

      1. 우선 부하가 꾸준히 많은 경우에는 애초부터 Replica 수를 늘리면 되기 때문에 HPA 설정의 의미가 크지 않다고 생각합니다.

      2. 만약 서비스의 특성이 이벤트 성이 강하고 특정 기간에만 부하가 많이 몰린다고 한다면 HPA 설정이 큰 힘이 될 것 같습니다.

      3. HPA 테스트를 해 본 결과, 60~70 정도로 메트릭을 설정하면 크게 부하가 없는데도 pod가 늘어나는 것을 보았습니다. 따라서 저는 90정도로 임계치를 높게 잡았습니다.

      4. 이 때 중요한 것은 애플리케이션 부팅 시간인데요. 부하를 빨리 분산시켜주려면 늘어나는 pod들이 빠르게 부팅되어야 합니다.

        따라서 이런 특성을 가진 서비스는 최대한 가볍게 만들어서 부팅시간을 짧게 구현하면 큰 도움이 될 것 같습니다.

        (Spring Boot 보다 부팅시간이 더 빠른 Node.js 등 다른 프레임워크를 사용하는 것도 추천)

      지금까지 긴 글을 읽어주셔서 감사합니다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      dragonpipe 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기