Kubernetes Monitoring – 3. kube-prometheus 설치 후 해야 할일

kube-prometheus 를 설치 후 해야할 일들에 대해서 알아 보겠습니다.

편안하고 안전한 모니터링을 위해서 다음과 같은 작업을 하면 좋습니다.

  • prometheus-k8s, alertmanager-main 에서 PV(Persistence Volume) 을 사용하도록 변경합니다.
  • prometheus-k8s, alertmanager-main, grafana를 위한 Ingress 를 생성합니다.
  • alertmanager-main 에서 노티피케이션을 보낼 수 있도록 설정을 추가해 줍니다.

PV(Persistence Volume) 사용하기

프로메테우스 서버 같은 애플리케이션들은 데이터를 스토리지에 저장합니다. 그래서, 포드(POD)를 이용하여 애플리케이션을 실행할 때, 사용할 수 있는 스토리지를 지정해 줘야 데이터를 저장할 수 있습니다. kube-prometheus 의 기본값을 사용하여 설치하였을 경우에, 설정된 스토리지는 호스트 노드의 임시 디렉토리입니다. 그래서 포드가 다른 곳에 재배치되는 등의 상황에서는 기존 데이터를 사용할 수 없는 문제가 생깁니다. 이러한 문제를 해결하기 위해서 쿠버네티스에 지원하는 볼륨들 중에서 영속성을 가지고, 노드에 상관없이 이용할 수는 볼륨을 사용해야 합니다. 이 문서에서는 쿠버네티스트의 PV(Persistence Volume)를 사용하겠습니다. PV는 단어 그대로, 영속성을 가지는 볼륨으로서, 데이터를 지속적으로 저장할 수 있습니다. 한 가지 주의할 점은, PV를 사용하기 위해서는 쿠버네티스 클러스터에 PV를 위한 프로비저닝 설정이 되어 있어야합니다. PV를 위한 동적 프로비저닝이 된다는 가정하에 설명을 진행하도록 하겠습니다.

먼저 쿠버네티스 클러스터에서 지원하는 스토리지 클래스를 조회해 봅니다. 다음 명령어를 실행하면 스토리지 클래스를 조회할 수 있습니다.

kubectl get storageclass

지원하는 스토리지 클래스가 있을 경우 다음과 같은 형식으로 출력됩니다.

NAME                   PROVISIONER                AGE
ssd (default)          cinder.csi.openstack.org   51d

prometheus-k8s 에서 PV 사용하기

동적 프로비저닝을 이용하여 PV를 생성해 보겠습니다. 동적 프로비저닝을 이용하면, PVC(PersistentVolumeClaim)을 생성하면, 프로비저너가 자동으로 PV를 생성해 줍니다.

다음 명령어를 실행하여 k8s 라는 이름의 Prometheus 리소스를 편집하겠습니다.

kubectl -n monitoring edit prometheus k8s

Prometheus 리소스 매니페스트를 다음과 같이 수정합니다. Prometheus 리소스 매니페스트의 storage 필드에 volumeClaimTemplate 을 추가해 줍니다. 사용할 storageClassName 이름과 용량을 지정해 줍니다.

apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  labels:
    prometheus: k8s
  name: k8s
  namespace: monitoring
spec:
  externalUrl: <http://prometheus.my-domain.com/>
  storage:
    volumeClaimTemplate:
      spec:
        resources:
          requests:
            storage: 80Gi
        storageClassName: ssd

alertmanager-main 에서 PV 사용하기

다음 명령어를 실행하여 main이라는 이름의 AlertManager 리소스를 편집하겠습니다.

kubectl -n monitoring edit alertmanager main

AlertManager 리소스 매니페스트를 다음과 같이 수정합니다. AlertManager 리소스 매니페스트의 storage 필드에 volumeClaimTemplate 을 추가해 줍니다. 사용할 storageClassName 이름과 용량을 지정해 줍니다.

apiVersion: monitoring.coreos.com/v1
kind: Alertmanager
metadata:
  labels:
    alertmanager: main
  name: main
  namespace: monitoring
spec:
  externalUrl: <http://alertmanager.my-domain.com/>
  storage:
    volumeClaimTemplate:
      spec:
        resources:
          requests:
            storage: 10Gi
        storageClassName: ssd

Grafana 에서 PV 사용하기

그라파나의 대시보드 같은 정보를 저장하기 위하여서 PV를 사용하는게 좋습니다. 기본 설정은 임시디렉토로 되어 있습니다. 그래서, 그라파에서 새로운 대시보드를 생성할 경우 문제가 생길 수 있습니다. 포드가 재시작되는 경우 새로 생성한 대시보드 정보가 사라져 버릴 수 있기 때문입니다. 그래서 그라파나에서 PV를 사용하도록 설정해 주는 좋습니다.

그라파나는 prometheus-operator 에서 관리하는 대상이 아닙니다. 쿠버네티스의 기본 리소스인 Deployment 를 사용하고 있습니다. 그래서 먼저 PVC 를 생성하여, Deployment 에 추가 해줘햐합니다.

PVC 생성하기

먼저 그라파나 PVC 매니페스트를 작성합니다.

grafana-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  labels:
    app: grafana
  name: grafana
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: ssd

그리고, 다음 명령어를 실행하여 grafan이라는 이름의 PVC 를 생성하겠습니다.

kubectl -n monitoring apply -f grafana-pvc.yaml

그라파나 Deployment 수정하기

다음 명령어를 실행하여 grafan이라는 이름의 Deployment 리소스를 편집하겠습니다.

kubectl -n monitoring edit deploy grafana

Deployment 리소스 매니페스트를 다음과 같이 수정합니다.

Deployment 리소스 매니페스트의 spec.template.spec.volumes 필드에 있는 name: grafana-storage 부분을 persistentVolumeClaim 을 사용하도록 수정해 줍니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: grafana
  name: grafana
...
spec:
...
  template:
    spec:
...
      volumes:
      - name: grafana-storage
        persistentVolumeClaim:
          claimName: grafana

그리고 디렉토리의 권한을 맞추기 위해서 securityContext을 수정해 줍니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: grafana
  name: grafana
...
spec:
...
  template:
    spec:
      securityContext:
        runAsUser: 472
        fsGroup: 472

이 부분은 그라파나 컨테이너 이미지 버전에 따라서 다르기 때문에 필요 없을 수도 있습니다. 만약 추가하지 않으면 다음과 같은 에러가 발생할 수 있습니다.

$ kubectl logs grafana-84b4f94f5-7hnl9

GF_PATHS_DATA='/var/lib/grafana' is not writable.
You may have issues with file permissions, more information here: <http://docs.grafana.org/installation/docker/#migration-from-a-previous-version-of-the-docker-container-to-5-1-or-later>
mkdir: can't create directory '/var/lib/grafana/plugins': Permission denied

다음은 수정한 매니페스트의 일부분입니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: grafana
  name: grafana
...
spec:
  replicas: 1
  selector:
    matchLabels:
      app: grafana
  template:
    metadata:
      labels:
        app: grafana
    spec:
      containers:
      - image: grafana/grafana:7.0.3
        name: grafana
        ports:
        - containerPort: 3000
          name: http
...
      nodeSelector:
        beta.kubernetes.io/os: linux
      securityContext:
        runAsUser: 472
        fsGroup: 472
      serviceAccountName: grafana
      volumes:
      - name: grafana-storage
        persistentVolumeClaim:
          claimName: grafana

Kubernetes Monitoring – 2. 모니터링 이해하기

프로메테우스를 이용한 쿠버네티스 모니터링 이해하기

kube-prometheus에 의해서 설치된 컴포넌트들을 살펴보도록 하겠습니다.

설치된 컴포넌트

kube-prometheus를 설치하면, 다음과 같은 컴포넌트들이 생성됩니다.

  • prometheus -operator : prometheus 와 alertmanager를 생성/삭제하고 관리하는 prometheus -operator가 설치됩니다.
  • prometheus : prometheus-k8s 라는 이름의 기본 프로메테우스 서버가 생성됩니다.
  • alertmanager : alertmanager-main 라는 이름의 기본 알럿 매니저가 생성됩니다.
  • node-exporter : 노드의 메트릭을 수집하기 위한 node-exporter가 생성됩니다.
  • prometheus -adapter : HPA를 위한 proemtheus-adapter가 생성됩니다.
  • kube-state-metrics : 쿠버네티스 클러스터의 메트릭을 수집하기 위한 kube-state-metrics가 생성됩니다.
  • grafana : 메트릭 시각화를 위한 grafana가 생성됩니다.

메트릭 수집

기본적으로 생성되는 prometheus-k8s 라는 이름의 프로메테우스 서버에서 수집하는 대상들에 대해서 알아보겠습니다. 설치한 버전에 따라서 약간의 차이는 존재할 수 있습니다.

프로메테우스 서버에서 수집하는 대상은 프로메테우스 서버 웹 화면의 메뉴에서 Status → Targets을 클릭하면 확인해 볼 수 있습니다. kube-prometheus 를 이용하여 설치했기 때문에, 기본적인 수집 대상이 등록되어 있습니다.

kubelet

쿠버네티스의 각 노드들에는 kubelet 이 기본적으로 설치되어 있습니다. kubelet 에는 cAdvisor가 통합되어 있는데, 이 cAdvisor 를 통해서 개별 컨테이너의 메트릭 정보들을 가져올 수 있습니다.

프로메테우스 서버는 kubelet을 이용하여 컨테이너의 메트릭 정보들을 수집합니다.

node-exporter

node-exporter는 쿠버네티스의 DaemonSet 리소스로 정의되어 있습니다. 그래서 쿠버네티스의 각 노드들에 node-exporter가 설치됩니다. node-exporter는 설치된 노드의 CPU, 메모리, 네트워크 같은 메트릭을 수집하도록 도와줍니다. node-exporter에 대한 자세한 사항은 해당 페이지를 참고하시기 바랍니다.

프로메테우스 서버는 node-exporter을 이용하여 노드의 메트릭 정보들을 수집합니다.

kube-state-metrics

kube-state-metrics 는 쿠버네티스 클러스터 레벨의 메트릭을 수집할 수 있도록 도와줍니다. 디플로이먼트(deployment)나 포드(pod) 등의 메트릭과 자원 예약 같은 정보를 제공합니다. kube-state-metrics에서 제공하는 메트릭에 대한 자세한 내용은 해당 페이지를 참고 바랍니다.

프로메테우스 서버는 kube-state-metrics을 이용하여 쿠버네티스 클러스터의 메트릭 정보들을 수집합니다.

기타 내부 컴포넌트

kube-system 네임스페이스 있는 내부 컴포넌트들의 메트릭도 수집을 합니다. etcd, coredns, kube-apiserver, kube-controller-manager, kube-scheduler 등이 있습니다.

알럿 관리

프로메테우스 서버에서 알럿 룰(AerltRule)을 정의하여, 알럿을 발생 시킬 수 있습니다. 발생한 알럿은 서버에 설정된 알럿 매니저(AlertManager)로 전송됩니다. prometheus-k8s 프로메테우스 서버의 경우에는 alertmanager-main 이라는 이름의 알럿 매니저로 알럿이 발송됩니다.

알럿 룰 (Alert Rules)

알럿 룰은 프로메테우스 서버에서 정의할 수 있습니다. 알럿 룰은 일반적으로 yaml 파일으로 이루어져 있으며 프로메테우스 서버가 해당 파일을 읽어서 알럿 룰을 읽어오게 됩니다.

알럿 룰은 프로메테우스 서버 웹 화면의 메뉴에서 Status → Rules 을 클릭하면 확인해 볼 수 있습니다. kube-prometheus 를 이용하여 설치했기 때문에, 기본적인 알럿 룰들이 등록되어 있습니다.

그리고 룰에 의해서 생성된 알럿들은 웹 화면의 메뉴의 Alerts 에서 확인할 수 있습니다.

알럿 룰은 알럿 룰을 정의한 yaml 파일은 만든 다음에, 프로메테우스 서버 설정에 추가 하면 됩니다. 이 글에서는 prometheus-operator를 사용하기 때문에, PrometheusRule 라는 사용자 리소스를 이용하여 알럿 룰을 정의할 수 있습니다.

다음은 PrometheusRule 매니페스트의 일부분 입니다.

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  labels:
    prometheus: k8s
    role: alert-rules
  name: prometheus-k8s-rules
  namespace: monitoring
spec:
  groups:
  - name: node-exporter
    rules:
    - alert: NodeFilesystemSpaceFillingUp
      annotations:
        description: Filesystem on {{ $labels.device }} at {{ $labels.instance }}
          has only {{ printf "%.2f" $value }}% available space left and is filling
          up.
        runbook_url: https://github.com/kubernetes-monitoring/kubernetes-mixin/tree/master/runbook.md#alert-name-nodefilesystemspacefillingup
        summary: Filesystem is predicted to run out of space within the next 24 hours.
      expr: |
        (
          node_filesystem_avail_bytes{job="node-exporter",fstype!=""} / node_filesystem_size_bytes{job="node-exporter",fstype!=""} * 100 < 40
        and
          predict_linear(node_filesystem_avail_bytes{job="node-exporter",fstype!=""}[6h], 24*60*60) < 0
        and
          node_filesystem_readonly{job="node-exporter",fstype!=""} == 0
        )
      for: 1h
      labels:
        severity: warning

프로메테우서 서버에서 발생한 알럿은 알럿 매니저로 발송됩니다. 알럿 매니저는 해당 알럿을 받아서, 설정에 따라 노티피케이션을 발송하게 됩니다. 노티피케이션은 슬랙, 메일 등 다양한 곳으로 보낼 수 있습니다. 자세한 사항은 해당 페이지를 참고하시기 바랍니다.

기본적으로 생성된alertmanager-main 알럿 매니저에서 별다른 설정이 없기 때문에, 노티피케이션이 발송되지 않습니다.

다음은 알럿 매니저의 화면입니다.

메트릭 시각화

프로메테우스 서버의 웹 화면에서 간단한 그래프를 그릴 수 있지만, 전문 시각화 도구인 그라파나(Grafana)를 이용하면, 보다 편리하고 멋지게 시각화를 할 수 있습니다.

그라파나는 데이터를 가져올 수 있는 데이터 소스를 등록할 수 있습니다. kube-prometheus 를 이용하여 설치했기 때문에 기본적으로 proemtheus-k8s 가 데이터 소스로 등록되어 있습니다. 그리고, 기본적인 대시보드들도 등록되어 있기 때문에, 바로 쿠버네티스 클러스터의 상태를 대시보드로 확인할 수 있습니다.

다음은 기본적으로 등록되어 있는 대시보드 목록의 일부분입니다. 대시보드 이름을 클릭하면 대시보드 화면으로 이동합니다.

다음은 쿠버네티스 클러스터의 대시보드 화면입니다.

Kubernetes Monitoring – 1. kube-prometheus 설치하기

쿠버네티스 클러스터와 애플리케이션을 모니터링 하기 위한 방법은 다양하게 존재합니다. 퍼블릭 클라우드를 사용하는 경우에는, 클라우드 프로바이더에서 제공하는 모니터링 도구들을 사용할 수 있습니다. 그리고, 직접 쿠버네티스 클러스터를 설치하여 사용하는 경우에는, 오픈소스들을 사용하여 모니터링할 수 있습니다. 이 글에서는 모니터링 오픈소스 중의 하나인 프로메테우스를 이용하여, 모니터링을 하는 방법에 대해서 알아보겠습니다.

쿠버네티스 기본 메트릭 서버

쿠버네티스에서는 리소스의 메트릭을 조회할 수 있는 메트릭 API를 제공하고 있습니다. 메트릭 API를 사용하면, 컨테이너 CPU 및 메모리 사용량과 같은 리소스 사용량을 조회할 수 있습니다. 이 메트릭 API는 kubectl top 같은 명령어를 실행될때 사용되며, HPA(Horizontal Pod Autoscaler) 같은 클러스터 컨트롤러에서도 사용을 하고 있습니다.

메트릭 API

메트릭 API를 통해 지정한 노드나 파드에서 사용하고 있는 현재 리소스의 양을 알 수 있습니다. 하지만, 메트릭 API는 메트릭 값을 저장하고 있지 않으므로, 10분 전에 사용한 리소스의 양을 조회할 수 는 없습니다. 한 가지 주의할 점은 메트릭 API를 사용하려면, 메트릭 서버가 클러스터에 배포되어 있어야합니다.

메트릭 API에 대한 상세한 정보는 k8s.io/metrics 리포지터리에서 확인할 수 있습니다.

메트릭 서버

메트릭 서버는 클러스터 전역에서 리소스 사용량 데이터를 집계합니다. 메트릭 서버는 오토스케일링 의 목적으로만 사용해야 합니다. 그래서 모니터링 솔루션에 메트릭스를 전달하거나, 모니터링 솔루션 메트릭의 소스로 사용해서는 안됩니다.

프로메테우스 서버

프로메테우스 소개

프로메테우스는 현재 쿠버네티스 상에서 가장 많이 사용되고 있는 오픈 소스 기반 모니터링 시스템입니다. CNCF에 소속되어 있으며, 쿠버네티스 클러스터 및 컨테이너들를 손쉽게 모니터링 가능합니다.

프로메테우스의 주요 특징은 다음과 같습니다.

  • 메트릭 이름과 key-value 형태로 식별되는 시계열 데이터를 제공합니다.
  • PromQL 이라는 쿼리 언어를 사용할 수 있습니다.
  • 경고(Alert)와 룰셋(Ruleset)을 만들 수 도 있습니다.
  • Grafana 같은 시스템을 이용하여 간단하게 대시보드를 만들 수 있습니다.

프로메테우스 구조

모니터링을 위해 메트릭을 수집하는 방식은 크게 두 가지가 있습니다. Push 방식과 Pull 방식입니다.

Push 방식은 각 서버나 애플리케이션에 클라이언트를 설치하고, 이 클라이언트가 메트릭 데이터를 수집해서 메트릭 서버로 보내는 방식입니다.

Pull 방식은 각 서버나 애플리케이션이 메트릭을 수집할 수 있는 엔드포인트를 제공합니다. 그래서 메트릭 서버가 해당 엔드포인트를 호출하여 메트릭을 가지고 가는 방식입니다.

프로메테우스는 Pull 방식을 사용합니다. 즉, 애플리케이션이 작동하고 있으면, 메트릭 서버가 주기적으로 애플리케이션의 메트릭 엔드포인트에 접속해서 데이터를 가져오는 방식을 취하는 것입니다.

다음 다이어그램은 프로메테우스의 구조와 일부 생태계 구성요소를 보여줍니다.

출처 : https://prometheus.io/docs/introduction/overview/

프로메테우스 생태계는 여러 컴포넌트로 구성되어 있으며, 대부분은 선택 사항입니다.

  • Prometeus Server : 시계열 데이터를 스크랩하고 저장합니다.
  • Service discovery : 프로메테우스는 메트릭을 Pull 하기 때문에, 메트릭 수집 대상에 대한 정보가 필요합니다. 메트릭 수집 대상은 파일 같은 것을 이용하여 직접 관리할 수 있고, 쿠버네티스와 연동하여 자동으로 수집 대상을 동기화할 수 있습니다.
  • Pushgateway : ‘Short-lived jobs’ 을 지원하기 위해서 메트릭을 Push 하기 위한 게이트입니다. 애플리케이션이 Pushgateway에 메트릭을 Push 한 후, Prometheus Server가 Pushgateway에 접근해 메트릭을 Pull 해서 가져옵니다.
  • Jobs/Expoerters : Exporter 는 프로메테우스가 메트릭을 수집할 수 있도록, 특정 서버나 애플리케이션의 메트릭을 노출할 수 있게 도와주는 에이전트라고 볼 수 있습니다. Exporter는 서버 상태를 나타내는 Node exporter 같은 것이 존재하며, 다양한 커스텀 Exporter 이 개발되어 사용되고 있습니다.이러한 Exporter를 사용하여, 메트릭을 프로메테우스어 수집해 갈 수 있습니다.
  • Alertmanager : 경고(Alert)를 관리합니다. 메트릭에 대한 어떠한 지표를 지정해놓고, 그 규칙을 위반하는 사항에 대해 경고을 전송하는 역할을 합니다. 발생한 경고를 Slack이나 Email등으로 발송할 수 있습니다.
  • Service discovery : 프로메테우스는 메트릭을 Pull 하기 때문에, 메트릭 수집 대상에 대한 정보가 필요합니다. 메트릭 수집 대상은 파일 같은 것을 이용하여 직접 관리할 수 있고, 쿠버네티스와 연동하여 자동으로 수집 대상을 동기화할 수 있습니다.
  • Data visualiztion : Data visualiztion은 다양한 모니터링 대시보드를 위한 시각화를 제공합니다. 프로메테우스 web UI에서도 수집한 데이터를 이용하여 간단한 그래프를 그릴 수 있습니다. 일반적으로 Grafana 같은 전문 시각환 도구를 이용하여, 수집한 데이터를 시각화 합니다.

쿠버네티스에서 프로메테우스 사용하기

쿠버네티스에서 프로메테우스를 사용하는 방법은 크게 두 가지가 있습니다.

  • Prometheus : 프로메테우스 서버를 직접 생성하여 사용하는 방법입니다.
  • Prometheus Operator : 프로메테우스 오퍼레이터를 먼저 설치 한 다음, 오퍼레이터를 이용하여 프로메테우스 서버를 생성하는 방법입니다.

이 글에서는 프로메테우스 오퍼레이터를 이용합니다. 프로메테우스 오퍼레이터를 사용하는 이유는, 보다 간단하게 프로메테우스 서버를 생성할 수 있기 때문입니다. 사용자의 다양한 요구를 만족시키거나, 자동화를 하기에서는 프로메테우스 오퍼레이터가 조금 더 편합니다. 하지만 프로메테우스 오퍼레이터에 대한 학습 비용이 더 발생하기 때문에 간단히 사용할 경우에는 직접 프로메테우스 서버를 생성하여 사용하는게 좋을 수도 있습니다.

Prometheus Operator

프로메테우스 오퍼레이터는 쿠버네티스의 서비스들을 쉽게 모니터링할 수 있도록 해줍니다. 그리고, 프로메테우스 인스턴스의 배포와 관리하는 기능을 제공하고 있습니다. 사용자는 쿠버네티스의 사용자 리소스를 이용하여, 프로메테우스 모니터링 인스턴스를 생성, 구성 및 관리할 수 있습니다.

프로메테우스 오퍼레이터는 설치 후 다음과 같은 기능을 제공한다.

  • 생성 / 삭제 : 프로메테우스 오퍼레이터를 사용하여, 특정 애플리케이션 또는 팀을 위한 프로메테우스 인스턴스를 쉽게 실행할 수 있습니다.
  • 단순 구성: 쿠버네티스의 리소스를 이용하여 프로메테우스의 설정을 구성할 수 있습니다.
  • 레이블을 통한 대상 서비스 : 쿠버네티스 레이블 쿼리를 기반으로 모니터링 대상 구성을 자동으로 생성할 수 있습니다.

Prometheus Operator vs. kube-prometheus vs. community helm chart

프로메테우스 오퍼레이터를 설치하는 방법도 크게 세 가지가 있습니다. 이 글에서 kube-prometheus 를 사용하겠습니다.

Prometheus Operator

프로메테우스 오퍼레이터는 프로메테우스와 Alertmanager를 관리하고 운영합니다.

kube-prometheus

kube-prometheus 프로메테우스 오퍼레이터와 일련의 매니페스트들을 결합하여 쿠버네티스 클러스트와 그 위에서 실행 중인 애플리케이션을 모니터링하는 것을 도와줍니다.

kube-prometheus 패키지에는 다음과 같은 패키지가 포함되어 있습니다.

helm chart

stable/prometheus-operator 헬름 차트는 kube-prometheus 와 비슷한 기능을 제공합니다. 이 차트는 커뮤니티에 의해 유지되고 있습니다. 자세한 내용은 차트의 readme 를 참고하십시오.

kube-prometheus

쿠버네티스 호환성

다음은 쿠버네티스 버전과 kube-prometheus 버전의 호환성을 나타낸 것입니다.

kube-prometheus stackKubernetes 1.14Kubernetes 1.15Kubernetes 1.16Kubernetes 1.17Kubernetes 1.18
release-0.3
release-0.4✔ (v1.16.5+)
release-0.5
HEAD

kube-prometheus 설치

이미 만들어진 매니페스트를 이용하여 kube-prometheus를 설치하겠습니다.

  • 저장소에서 kube-prometheus 를 가지고 옵니다.
git clone <https://github.com/coreos/kube-prometheus.git>
cd kube-prometheus
  • 디렉토리에 있는 매니페스트를 사용하여 모니터링 스택을 생성합니다.
kubectl create -f manifests/setup
until kubectl get servicemonitors --all-namespaces ; do date; sleep 1; echo ""; done
kubectl create -f manifests/

모니터링 컴포넌트를 배포할 때 레이스 조건을 피하기 위해 먼저 네임스페이스와 CustomResourceDefinitions를 생성합니다. 두 폴더의 리소스를 단일 명령 kubectl create -f manifests/setup -f manifests로 적용할 수 있지만, 모든 컴포넌트가 성공적으로 생성되기 위해서는 명령을 여러 번 실행해야 할 수도 있기 때문에 나누어서 실행하였습니다.

  • 만약 설치한 모니터링 스택을 제거하려면 다음 명령어를 사용할 수 있습니다.
kubectl delete --ignore-not-found=true -f manifests/ -f manifests/setup

대시보드 접근하기

Prometheus

$ kubectl --namespace monitoring port-forward svc/prometheus-k8s 9090

http://localhost:9090 로 접속할 수 있습니다.

Grafana

$ kubectl --namespace monitoring port-forward svc/grafana 3000

http://localhost:3000 로 접속할 수 있습니다. Grafana의 기본 사용자:비밀번호는 admin:admin 입니다.

Alert Manager

$ kubectl --namespace monitoring port-forward svc/alertmanager-main 9093

Then access via http://localhost:9093 로 접속할 수 있습니다.

참고자료

Kubernetes Container Timezone

쿠버네티스 컨테이너 타임존 변경하기

컨테이너를 이용하여 애플리케이션을 실행시킬 때 타임존을 변경해야 하는 경우가 생길 수 있습니다. 예를 들어, 로그를 남길 때 시간을 기록하게 되는데, 이 시간이 자신의 타임존과 다르다면 많이 불편할 것입니다. 이럴 경우 컨테이너의 타임존을 자신이 속한 타임존으로 변경해주면, 개발자는 좀더 효과적으로 로그를 확인할 수 있습니다.

이 글에서는 컨테이너의 타임존을 변경하는 방법을 이용하여, 쿠버티스 포드(POD)를 실행할때 타임존은 변경해보도록 하겠습니다

기본 타임존 (UTC +0)

먼저 busybox 컨테이너 이미지를 이용하여 POD 를 생성해 보겠습니다.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: default-timezone
spec:
  containers:
  - image: busybox
    name: busybox
    args:
    - sleep
    - "100000"
EOF

생성한 default-timezone 포드가 정상적으로 작동되면, 실행중인 컨테이너의 shell에 접속해서 date 명령어를 실행해 보겠습니다.

kubectl exec -it default-timezone sh
/ # date
Tue Jun  9 11:55:56 UTC 2020

/ # strings /etc/localtime
TZif2
TZif2
UTC0

/ # exit

서울 타임존 (UTC +9)

이제 타임존을 UTC에서 한국의 서울(Asia/Seoul)로 변경해 보겠습니다.

새로운 타임존을 컨테이너에 주입하기 위하여, volumeMountsvolumes 을 사용하였습니다. volumes에서 hostPath 를 이용하여 호스트 노드에 있는 /usr/share/zoneinfo/Asia/Seoul 을 포드에 추가합니다. 그런 다음 컨테이너에서 volumeMounts 필드를 이용하여 추가한 볼륨을 컨테이너의 /etc/localtime 로 마운트합니다. 다시 말해서, 호스트 노드에 있는 /usr/share/zoneinfo/Asia/Seoul 을 컨테이너의 /etc/localtime 로 마운트하는 것입니다.

다음은 타임존을 서울로 설정한 포드 예제입니다. 예제를 실행하면, seoul-timezone 포드가 생성됩니다.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name : seoul-timezone
spec:
  containers:
  - image: busybox
    name: busybox
    args:
    - sleep
    - "100000"
    volumeMounts:
    - name: tz-seoul
      mountPath: /etc/localtime
  volumes:
    - name: tz-seoul
      hostPath:
        path: /usr/share/zoneinfo/Asia/Seoul
EOF

생성한 seoul-timezone 포드가 정상적으로 작동되면, 실행중인 컨테이너의 shell에 접속해서 date 명령어를 실행해 보겠습니다.

kubectl exec -it seoul-timezone sh
/ # date
Tue Jun  9 20:58:01 KST 2020

/ # strings /etc/localtime
TZif2
5qx
TZif2
KST-9

/ # exit

타임존 환경변수 설정

대부분의 경우 앞서 설명한 방법만으로 충분하지만, 혹시 안되는 경우가 있다면 다음과 같이 TZ 라는 환경 변수에 타임존을 직접 지정해 줄 수 있습니다.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name : seoul-timezone
spec:
  containers:
  - image: busybox
    name: busybox
    env:
    - name: TZ
      value: Asia/Tokyo
    args:
    - sleep
    - "100000"
    volumeMounts:
    - name: tz-seoul
      mountPath: /etc/localtime
  volumes:
    - name: tz-seoul
      hostPath:
        path: /usr/share/zoneinfo/Asia/Seoul
EOF

SpringBoot(자바) 애플리케이션에서 타임존 설정

다음은 SpringBoot 애플리케이션에 타임존 변경을 추가한 디플로이먼트 매니페스트 예제입니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: api-server
    app.kubernetes.io/component: server
    app.kubernetes.io/part-of: example
  name: api-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: api-server
      app.kubernetes.io/component: server
      app.kubernetes.io/part-of: example
  template:
    metadata:
      labels:
        app.kubernetes.io/name: api-server
        app.kubernetes.io/component: server
        app.kubernetes.io/part-of: example
    spec:
      containers:
        - env:
            - name: JAVA_TOOL_OPTIONS
              value: "-Xms1024m -Xmx2048m"
            - name: SPRING_PROFILES_ACTIVE
              value: develop
            - name: TZ
              value: Asia/Seoul
          image: kangwoo/api-server
          imagePullPolicy: Always
          name: api-server
          ports:
            - containerPort: 8080
              name: http
              protocol: TCP
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /actuator/health
              port: 8089
              scheme: HTTP
            initialDelaySeconds: 60
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /actuator/info
              port: 8089
              scheme: HTTP
            initialDelaySeconds: 60
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
          volumeMounts:
            - name: tz-seoul
              mountPath: /etc/localtime
          resources:
            limits:
              cpu: "1"
              memory: 2Gi
            requests:
              cpu: "1"
              memory: 2Gi
      dnsPolicy: ClusterFirst
      volumes:
        - name: tz-seoul
          hostPath:
            path: /usr/share/zoneinfo/Asia/Seoul
      restartPolicy: Always
      terminationGracePeriodSeconds: 30

참고로, 자바 애플리케이션의 경우 TZ라는 환경 변수 대신에, system property로 다음과 같이 추가할 수도 있습니다.

-Duser.timezone=Asia/Seoul

Seldon Core – Tensorflow Serving

학습이 완료된 Tensorflow 모델을 저장 한 경우, Seldon의 사전 패키지 된 TensorFlow 서버를 사용하여 간단히 배포 할 수 있습니다.

전제 조건

  • REST의 경우 다음에 대한 파라미터를지정해야합니다.
    • signature_name
    • model_name
apiVersion: machinelearning.seldon.io/v1alpha2
kind: SeldonDeployment
metadata:
  name: tfserving
spec:
  name: mnist
  predictors:
  - graph:
      children: []
      implementation: TENSORFLOW_SERVER
      modelUri: pvc://seldon-models-pvc/tensorflow/mnist/model
      name: mnist-model
      parameters:
        - name: signature_name
          type: STRING
          value: serving_default
        - name: model_name
          type: STRING
          value: mnist-model
    name: default
    replicas: 1

  • GRPC의 경우 다음에 대한 파라미터를 지정해야합니다.
    • signature_name
    • model_name
    • model_input
    • model_output
kind: SeldonDeployment
metadata:
  name: tfserving
spec:
  name: mnist
  predictors:
  - graph:
      children: []
      implementation: TENSORFLOW_SERVER
      modelUri: pvc://seldon-models-pvc/tensorflow/mnist/modell
      name: mnist-model
      endpoint:
        type: GRPC
      parameters:
        - name: signature_name
          type: STRING
          value: serving_default
        - name: model_name
          type: STRING
          value: mnist-model
        - name: model_input
          type: STRING
          value: images
        - name: model_output
          type: STRING
          value: scores
    name: default
    replicas: 1

모델 생성

Tensorflow 서버를 테스트 하려면 먼저 파이썬을 사용하여 간단한 Tensorflow 모델을 생성해야 합니다.

텐서플로우 모델을 만들고 훈련하기 위한 고수준 API인 tf.keras를 사용합니다. 모델의 save() 메소드를 이용하여, 전체 모델을 지정한 위치에 저장합니다. 여기에는 가중치, 모델 구성 등이 포함됩니다. 모델을 저장할 때 주의해야할 점은 모델 저장 위치의 마지막 디렉토리에 모델의 버전이 포함되어야 합니다. 모델 버전은 숫자를 사용해야합니다.

케라스의 데이터셋 중의 하나인 mnist 데이터를 분류하는 모델을 작성해 보겠습니다.

from __future__ import absolute_import, division, print_function, unicode_literals

import argparse
import os

import tensorflow as tf

def train():
    print("TensorFlow version: ", tf.__version__)

    parser = argparse.ArgumentParser()
    parser.add_argument('--model_path', default='/mnt/pv/tensorflow/mnist/model', type=str)
    args = parser.parse_args()

    version = 1
    export_path = os.path.join(args.model_path, str(version))

    (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
    x_train, x_test = x_train / 255.0, x_test / 255.0

    model = tf.keras.models.Sequential([
        tf.keras.layers.Flatten(input_shape=(28, 28)),
        tf.keras.layers.Dense(128, activation='relu'),
        tf.keras.layers.Dropout(0.2),
        tf.keras.layers.Dense(10, activation='softmax')
    ])

    model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=0.01),
                  loss='sparse_categorical_crossentropy',
                  metrics=['accuracy'])

    print("Training...")
    training_history = model.fit(x_train, y_train, batch_size=64, epochs=10,
                                 validation_split=0.2)

    print('\\nEvaluate on test data')
    results = model.evaluate(x_test, y_test, batch_size=128)
    print('test loss, test acc:', results)

    model.save(export_path)
    print('"Saved model to {}'.format(export_path))

if __name__ == '__main__':
    train()

생성 된 모델을 사용하여 Tensorflow 서버를 실행하고 예측을 수행 할 수 있습니다. 모델은 PV, S3 호환 가능 개체 저장소, Azure Blob 저장소 또는 Google Cloud Storage에 있을 수 있습니다.

모델 저장하기

쿠버네티스의 퍼시스턴스 볼륨에 모델을 저장해 보겠습니다. PVC 는 앞서 생성한 seldon-models-pvc 을 사용하겠습니다. 모델을 학습시키기 위해서 쿠버네티스 잡(Job)을 사용하겠습니다. Job을 생성할 때 모델을 저장하기 위한 PVC를 마운트 해줍니다.

모델 코드 작성하기

mnist 이미지를 분류하는 모델입니다. 모델을 저장할 위치를 --model_path 파라미터로 입력받게 하였습니다.

tensorflow_mnist.py

from __future__ import absolute_import, division, print_function, unicode_literals

import argparse
import os

import tensorflow as tf

def train():
    print("TensorFlow version: ", tf.__version__)

    parser = argparse.ArgumentParser()
    parser.add_argument('--model_path', default='/mnt/pv/tensorflow/mnist/model', type=str)
    args = parser.parse_args()

    version = 1
    export_path = os.path.join(args.model_path, str(version))

    (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
    x_train, x_test = x_train / 255.0, x_test / 255.0

    model = tf.keras.models.Sequential([
        tf.keras.layers.Flatten(input_shape=(28, 28)),
        tf.keras.layers.Dense(128, activation='relu'),
        tf.keras.layers.Dropout(0.2),
        tf.keras.layers.Dense(10, activation='softmax')
    ])

    model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=0.01),
                  loss='sparse_categorical_crossentropy',
                  metrics=['accuracy'])

    print("Training...")
    training_history = model.fit(x_train, y_train, batch_size=64, epochs=10,
                                 validation_split=0.2)

    print('\\nEvaluate on test data')
    results = model.evaluate(x_test, y_test, batch_size=128)
    print('test loss, test acc:', results)

    model.save(export_path)
    print('"Saved model to {}'.format(export_path))

if __name__ == '__main__':
    train()

컨테이너 이미지를 만들기

컨테이너 이미지를 만들기 위한 Dockerfile 입니다. 텐서플로우를 기본 이미지로 사용합니다.

Dockerfile

FROM tensorflow/tensorflow:2.1.0-py3

RUN mkdir -p /app
ADD tensorflow_mnist.py /app/

쿠버네티스 잡 실행하기

컨테이너 이미지를 빌드하고, 컨테이너 이미지 레지스트리에 푸시 한 다음, 쿠버네티스 잡(Job)을 생성하겠습니다.

Job을 생성할 때는 모델을 저장하기 위해서 PVC를 마운트 해줍니다. 이 일련의 작업들은 직접 실행 할 수 있습니다. 하지만 좀 더 편하게 하기 위해서 앞서 배운 Kubeflow Fairing을 사용하겠습니다.

다음은 로컬 개발 환경에서 Fairing을 사용하여 컨테이너 이미지를 만들고, 쿠버네티스 잡을 실행하는 예제입니다.

fairing-local-docker.py

import uuid

from kubeflow import fairing
from kubeflow.fairing.kubernetes import utils as k8s_utils

CONTAINER_REGISTRY = 'kangwoo'

namespace = 'admin'
job_name = f'tensorflow-mnist-job-{uuid.uuid4().hex[:4]}'

command = ["python", "tensorflow_mnist.py", "--model_path", "/mnt/pv/tensorflow/mnist/models"]
output_map = {
    "Dockerfile": "Dockerfile",
    "tensorflow_mnist.py": "tensorflow_mnist.py"
}

fairing.config.set_preprocessor('python', command=command, path_prefix="/app", output_map=output_map)

fairing.config.set_builder('docker', registry=CONTAINER_REGISTRY, image_name="tensorflow-mnist",
                           dockerfile_path="Dockerfile")

fairing.config.set_deployer('job', namespace=namespace, job_name=job_name,
                            pod_spec_mutators=[
                                k8s_utils.mounting_pvc(pvc_name='seldon-models-pvc', pvc_mount_path='/mnt/pv')],
                            cleanup=True, stream_log=True)

fairing.config.run()

fairing을 실행하면 쿠버네티스 잡이 생성되고, 학습이 완료된 모델이 지정한 경로에 저장됩니다.

Tensorflow을 사용하는 SeldonDeployment 로 배포 하기

SeldonDeployment 생성

SeldonDeployment 매니페스트를 작성합니다. predictor의 구현체를 SKLEARN_SERVER 로 사용합니다. modelUri 필드로 모델 저장 위치를 지정해 줍니다. pvc 의 이름이 kfserving-models-pvc 이고 저장 위치가 models/sklearn/iris 이므로, pvc://kfserving-models-pvc/models/sklearn/iris 라고 지정해 줍니다.

기본적으로 모델 서버는 로드한 모델의 predict_proba 메소드를 호출합니다. 만약 다른 메소드를 사용하고 싶다면 파라미터로 변경할 수 있습니다. 예를 들어 predict 메소드를 호출하게 하라면, parameters 섹션에 method 란 이름으로 값을 지정해 주면 됩니다. 다음 예제는 predict 메소드를 호출하게 설정하였습니다.

tensorflow-mnist.yaml

apiVersion: machinelearning.seldon.io/v1alpha2
kind: SeldonDeployment
metadata:
  name: tensorflow-mnist
spec:
  name: mnist
  predictors:
  - graph:
      children: []
      implementation: TENSORFLOW_SERVER
      modelUri: pvc://seldon-models-pvc/tensorflow/mnist/model
      name: mnist-model
      parameters:
        - name: signature_name
          type: STRING
          value: serving_default
        - name: model_name
          type: STRING
          value: mnist-model
    name: default
    replicas: 1

SeldonDeployment 를 생성합니다.

다음은 admin 네임스페이스 SeldonDeployment 를 생성하는 예제입니다.

kubectl -n admin apply -f tensorflow-mnist.yaml

생성한 SeldonDeployment를 조회해 보겠습니다.

kubectl -n admin get seldondeployment tensorflow-mnist -o yaml

SeldonDeployment 가 정상적으로 생성되면 다음과 같은 응답 결과를 확인할 수 있습니다.

apiVersion: machinelearning.seldon.io/v1
kind: SeldonDeployment
metadata:
  ...
spec:
  ...
status:
  deploymentStatus:
    mnist-default-725903e:
      availableReplicas: 1
      replicas: 1
  serviceStatus:
    mnist-default-mnist-model-seldonio-tfserving-proxy-rest-0-7:
      httpEndpoint: mnist-default-mnist-model-seldonio-tfserving-proxy-rest-0-7.admin:9000
      svcName: mnist-default-mnist-model-seldonio-tfserving-proxy-rest-0-7
    tensorflow-mnist-mnist-default:
      grpcEndpoint: tensorflow-mnist-mnist-default.admin:5001
      httpEndpoint: tensorflow-mnist-mnist-default.admin:8000
      svcName: tensorflow-mnist-mnist-default
  state: Available

SeldonDeploymentstateAvailable 이면 예측을 요청 할 수 있습니다.

예측 실행하기

예측을 요청하기 위해서는 모델 서버에 접근해야 합니다. 모델 서버는 ingressgateway 를 통해서 접근할 수 있습니다. ingressgateway 는 모델 서버들을 구분하기 위해서 호스트 이름을 사용합니다. ingressgateway에 접근하 기 위한 주소는 앞서 정의한 CLUSTER_IP 를 사용하겠습니다.

예측을 요청할 데이터를 json 파일로 작성합니다.

데이터의 크기가 크기 때문에 git 에 있는 파일을 다운받아서 사용해주세요.

mnist-input.json

{
  "data": {
    "ndarray": [
      [...],
       ...
      [...]
    ]
  }
}

다음은 admin 네임스페이스의 tensorflow-mnist SeldonDeployment 에 예측을 요청하는 예제입니다.

MODEL_NAME=tensorflow-mnist
NAMESPACE=admin

INPUT_PATH=@./mnist-input.json
curl -v -H "Content-Type: application/json" http://$CLUSTER_IP/seldon/${NAMESPACE}/${MODEL_NAME}/api/v1.0/predictions -d $INPUT_PATH

정상적으로 실행되면 다음과 같은 응답 결과를 확인 할 수 있습니다.

*   Trying 192.168.21.38...
* TCP_NODELAY set
* Connected to 192.168.21.38 (192.168.21.38) port 32380 (#0)
> POST /seldon/admin/tensorflow-mnist/api/v1.0/predictions HTTP/1.1
> Host: 192.168.21.38:32380
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 5725
> Expect: 100-continue
> 
< HTTP/1.1 100 Continue
* We are completely uploaded and fine
< HTTP/1.1 200 OK
< x-content-type-options: nosniff
< vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers
< content-type: application/json;charset=utf-8
< content-length: 470
< date: Thu, 09 Apr 2020 15:51:27 GMT
< x-envoy-upstream-service-time: 142
< server: istio-envoy
< 
{
  "meta": {
    "puid": "ufdopha1s5gnemt86h06d4jg5e",
    "tags": {
    },
    "routing": {
    },
    "requestPath": {
      "mnist-model": "seldonio/tfserving-proxy_rest:0.7"
    },
    "metrics": []
  },
  "data": {
    "names": ["t:0", "t:1", "t:2", "t:3", "t:4", "t:5", "t:6", "t:7", "t:8", "t:9"],
    "ndarray": [[3.01518681E-4, 1.00308341E-6, 4.13124333E-4, 0.00133548444, 4.15516388E-6, 7.8677E-5, 5.88266346E-7, 0.996478, 3.98369411E-5, 0.00134761049]]
  }
}

Seldon Core – MLflow Server

학습이 완료된 MLflow모델을 저장 한 경우, Seldon의 사전 패키지 된 MLflow 서버를 사용하여 간단히 배포 할 수 있습니다. 그리고 conda.yaml 파일을 이용하면, MLflow 서버의 초기화 중 Conda 환경을 만들 수 도 있습니다.

전제 조건

MLflow 서버를 사용하려면 다음 전제 조건이 충족되어야합니다.

MFLow에서 제공하는 모델 형태

MLFlow 에서 제공하고 있는 주요 모델 형태는 다음과 같습니다.

  • Python Function (python_function)
  • Keras (keras)
  • PyTorch (pytorch)
  • Scikit-learn (sklearn)
  • Spark MLlib (spark)
  • TensorFlow (tensorflow)
  • XGBoost (xgboost)

Conda 환경 생성

MLflow 서버는 여러가지 머신러닝 프레임워크를 지원합니다. 그래서 모델 서버를 실행할 때 필요한 패키지들을 설치할 필요가 있습니다. MLflow 서버는 conda.yaml 파일을 이용하여 모델 서버에 실행에 필요한 환경을 만듭니다. SeldonDeployment 에 의해서 모델 서버가 생성될 때, 모델 저장 위치에 있는 conda.yaml 파일을 읽어와서 Conda 환경을 구성합니다.

모델 생성

MLflow 서버를 사용하기 위해 파이썬을 사용한 간단한 scikit-learn 모델을 생성한 후, MLFlow를 이용하여 저장하겠습니다.

Scikit-learn의 기본적인 데이터셋 중의 하나인 아이리스 꽃 데이터를 사용하여, 아이리스 꽃을 분류하는 모델을 작성해 보겠습니다. mlflow.sklearn.save_model() 메소드를 이용하여 MLflow 형식으로 모델을 저장합니다.

from joblib import dump
from sklearn import datasets
from sklearn import svm

clf = svm.SVC(gamma='scale')
iris = datasets.load_iris()
X, y = iris.data, iris.target
clf.fit(X, y)
mlflow.sklearn.save_model(clf, path=model_path)

모델 저장하기

쿠버네티스의 퍼시스턴스 볼륨에 모델을 저장해 보겠습니다. PVC 는 앞서 생성한 seldon-models-pvc 을 사용하겠습니다. 모델을 학습시키기 위해서 쿠버네티스 잡(Job)을 사용하겠습니다. Job을 생성할 때 모델을 저장하기 위한 PVC를 마운트 해줍니다.

모델 코드 작성하기

아이리스 꽃을 분류하는 간단한 모델입니다. 모델을 저장할 위치를 --model_path 파라미터로 입력받게 하였습니다.

mlflow_sklearn_iris.py

import argparse
import os

import mlflow
import mlflow.sklearn
from sklearn import datasets
from sklearn import svm

def train():
    parser = argparse.ArgumentParser()
    parser.add_argument('--model_path', default='/mnt/pv/mlflow/sklearn/iris/model', type=str)
    args = parser.parse_args()

    # if not (os.path.isdir(args.model_path)):
    #     os.makedirs(args.model_path)
    # os.rmdir(args.model_path)

    clf = svm.SVC(gamma='scale')
    iris = datasets.load_iris()
    X, y = iris.data, iris.target
    clf.fit(X, y)
    print('Finished Training')

    conda_env = {
        'name': 'mlflow-env',
        'channels': ['defaults'],
        'dependencies': [
            'python=3.7.0',
            'scikit-learn=0.20.3'
        ]
    }

    mlflow.sklearn.save_model(clf, path=args.model_path, conda_env=conda_env)

if __name__ == '__main__':
    train()

컨테이너 이미지를 만들기

컨테이너 이미지를 만들기 위한 Dockerfile 입니다. 파이썬을 기본 이미지로 사용하고, scikit-learn 패키지를 추가로 설치합니다.

Dockerfile

FROM python:3.7-slim

RUN pip install scikit-learn==0.20.3 mlflow

RUN mkdir -p /app
ADD mlflow_sklearn_iris.py /app/

쿠버네티스 잡 실행하기

컨테이너 이미지를 빌드하고, 컨테이너 이미지 레지스트리에 푸시 한 다음, 쿠버네티스 잡(Job)을 생성하겠습니다.

Job을 생성할 때는 모델을 저장하기 위해서 PVC를 마운트 해줍니다. 이 일련의 작업들은 직접 실행 할 수 있습니다. 하지만 좀 더 편하게 하기 위해서 앞서 배운 Kubeflow Fairing을 사용하겠습니다.

다음은 로컬 개발 환경에서 Fairing을 사용하여 컨테이너 이미지를 만들고, 쿠버네티스 잡을 실행하는 예제입니다.

fairing-local-docker.py

import uuid
from kubeflow import fairing
from kubeflow.fairing.kubernetes import utils as k8s_utils

CONTAINER_REGISTRY = 'kangwoo'

namespace = 'admin'
job_name = f'mlflow-sklean-iris-job-{uuid.uuid4().hex[:4]}'

command=["python", "mlflow_sklearn_iris.py", "--model_path", "/mnt/pv/mlflow/sklearn/iris/model"]
output_map = {
    "Dockerfile": "Dockerfile",
    "mlflow_sklearn_iris.py": "mlflow_sklearn_iris.py"
}

fairing.config.set_preprocessor('python', command=command, path_prefix="/app", output_map=output_map)

fairing.config.set_builder('docker', registry=CONTAINER_REGISTRY, image_name="mlflow-sklean-iris", dockerfile_path="Dockerfile")

fairing.config.set_deployer('job', namespace=namespace, job_name=job_name,
                            pod_spec_mutators=[k8s_utils.mounting_pvc(pvc_name='seldon-models-pvc', pvc_mount_path='/mnt/pv')],
                            cleanup=True, stream_log=True)

fairing.config.run()

fairing을 실행하면 쿠버네티스 잡이 생성되고, 학습이 완료된 모델이 지정한 경로에 저장됩니다.

MLflow을 사용하는 SeldonDeployment 로 배포 하기

SeldonDeployment 생성

SeldonDeployment 매니페스트를 작성합니다. predictor의 구현체를 MLFLOW_SERVER 로 사용합니다. modelUri 필드로 모델 저장 위치를 지정해 줍니다. pvc 의 이름이 seldon-models-pvc 이고 저장 위치가 mlflow/sklearn/iris/model 이므로, pvc://seldon-models-pvc/mlflow/sklearn/iris/model 라고 지정해 줍니다.

mlflow-sklearn-iris.yaml

apiVersion: machinelearning.seldon.io/v1alpha2
kind: SeldonDeployment
metadata:
  name: mlflow-sklearn-iris
spec:
  name: mlflow-sklearn-iris
  predictors:
    - graph:
        children: []
        implementation: MLFLOW_SERVER
        modelUri: pvc://seldon-models-pvc/mlflow/sklearn/iris/model
        name: classifier
      name: default
      replicas: 1

SeldonDeployment 를 생성합니다.

다음은 admin 네임스페이스 SeldonDeployment 를 생성하는 예제입니다.

kubectl -n admin apply -f mlflow-sklearn-iris.yaml

생성한 SeldonDeployment를 조회해 보겠습니다.

kubectl -n admin get seldondeployment mlflow-sklearn-iris -o yaml

SeldonDeployment 가 정상적으로 생성되면 다음과 같은 응답 결과를 확인할 수 있습니다.

apiVersion: machinelearning.seldon.io/v1
kind: SeldonDeployment
metadata:
  ...
spec:
  ...
status:
  deploymentStatus:
    mlflow-sklearn-iris-default-8c791aa:
      availableReplicas: 1
      replicas: 1
  serviceStatus:
    mlflow-sklearn-iris-mlflow-sklearn-iris-default:
      grpcEndpoint: mlflow-sklearn-iris-mlflow-sklearn-iris-default.admin:5001
      httpEndpoint: mlflow-sklearn-iris-mlflow-sklearn-iris-default.admin:8000
      svcName: mlflow-sklearn-iris-mlflow-sklearn-iris-default
    seldon-d9062e953c9534d009e3dc84a7d3707a:
      httpEndpoint: seldon-d9062e953c9534d009e3dc84a7d3707a.admin:9000
      svcName: seldon-d9062e953c9534d009e3dc84a7d3707a
  state: Available

SeldonDeploymentstateAvailable 이면 예측을 요청 할 수 있습니다.

예측 실행하기

예측을 요청하기 위해서는 모델 서버에 접근해야 합니다. 모델 서버는 ingressgateway 를 통해서 접근할 수 있습니다. ingressgateway 는 모델 서버들을 구분하기 위해서 호스트 이름을 사용합니다. ingressgateway에 접근하 기 위한 주소는 앞서 정의한 CLUSTER_IP 를 사용하겠습니다.

예측을 요청할 데이터를 json 파일로 작성합니다.

iris-input.json

{
  "data": {
    "ndarray": [
      [6.8,  2.8,  4.8,  1.4],
      [6.0,  3.4,  4.5,  1.6]
    ]
  }
}

다음은 admin 네임스페이스의 sklearn-iris SeldonDeployment 에 예측을 요청하는 예제입니다.

MODEL_NAME=mlflow-sklearn-iris
NAMESPACE=admin

INPUT_PATH=@./iris-input.json
curl -v -H "Content-Type: application/json" http://$CLUSTER_IP/seldon/${NAMESPACE}/${MODEL_NAME}/api/v1.0/predictions -d $INPUT_PATH

정상적으로 실행되면 다음과 같은 응답 결과를 확인 할 수 있습니다.

*   Trying 192.168.21.38...
* TCP_NODELAY set
* Connected to 192.168.21.38 (192.168.21.38) port 32380 (#0)
> POST /seldon/admin/mlflow-sklearn-iris/api/v1.0/predictions HTTP/1.1
> Host: 192.168.21.38:32380
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 96
> 
* upload completely sent off: 96 out of 96 bytes
< HTTP/1.1 200 OK
< x-content-type-options: nosniff
< vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers
< content-type: application/json;charset=utf-8
< content-length: 261
< date: Thu, 09 Apr 2020 16:02:46 GMT
< x-envoy-upstream-service-time: 100
< server: istio-envoy
< 
{
  "meta": {
    "puid": "geuhtnu2stgad08ngp9c0382oi",
    "tags": {
    },
    "routing": {
    },
    "requestPath": {
      "classifier": "seldonio/mlflowserver_rest:0.2"
    },
    "metrics": []
  },
  "data": {
    "names": [],
    "ndarray": [1, 1]
  }
}

Seldon Core – XGBoost Server

학습이 완료된 XGBoost 모델을 저장 한 경우에는 Seldon의 사전 패키지 된 XGBoost 서버를 사용하여 간단히 배포 할 수 있습니다.

전제 조건

  • 모델 피클 파일명은 model.bst 이어야 합니다.
  • xgboost v0.82 버전을 사용합니다.

모델 생성

XGBoost 서버를 테스트하려면 먼저 파이썬을 사용하여 간단한 XGBoost 모델을 생성해야 합니다.

Scikit-learn의 기본적인 데이터셋 중의 하나인 아이리스 꽃 데이터를 사용하여, 아이리스 꽃을 분류하는 모델을 작성해 보겠습니다. 모델 피클의 이름 model.bst 이어야 합니다.

import xgboost as xgb
from sklearn.datasets import load_iris

iris = load_iris()
X = iris['data']
y = iris['target']
dtrain = xgb.DMatrix(X, label=y)
param = {'max_depth': 6,
         'eta': 0.1,
         'silent': 1,
         'nthread': 4,
         'num_class': 10,
         'objective': 'multi:softmax'
         }
xgb_model = xgb.train(params=param, dtrain=dtrain)
xgb_model.save_model('model.bst')

생성 된 모델을 사용하여 XGBoost 서버를 실행하고 예측을 수행 할 수 있습니다. 모델은 PV, S3 호환 가능 개체 저장소, Azure Blob 저장소 또는 Google Cloud Storage에 있을 수 있습니다.

모델 저장하기

쿠버네티스의 퍼시스턴스 볼륨에 모델을 저장해 보겠습니다. PVC 는 앞서 생성한 seldon-models-pvc 을 사용하겠습니다. 모델을 학습시키기 위해서 쿠버네티스 잡(Job)을 사용하겠습니다. Job을 생성할 때 모델을 저장하기 위한 PVC를 마운트 해줍니다.

모델 코드 작성하기

아이리스 꽃을 분류하는 간단한 모델입니다. 모델을 저장할 위치를 --model_path 파라미터로 입력받게 하였습니다.

iris.py

import argparse
import os

from joblib import dump
from sklearn import datasets
from sklearn import svm

def train():
    parser = argparse.ArgumentParser()
    parser.add_argument('--model_path', default='/mnt/pv/xgboost/iris/model', type=str)
    args = parser.parse_args()

    if not (os.path.isdir(args.model_path)):
        os.makedirs(args.model_path)

    model_file = os.path.join(args.model_path, 'model.joblib')

    clf = svm.SVC(gamma='scale')
    iris = datasets.load_iris()
    X, y = iris.data, iris.target
    clf.fit(X, y)
    dump(clf, model_file)

if __name__ == '__main__':
    train()

컨테이너 이미지를 만들기

컨테이너 이미지를 만들기 위한 Dockerfile 입니다. 파이썬을 기본 이미지로 사용하고, xgboostscikit-learn 패키지를 추가로 설치합니다.

Dockerfile

FROM python:3.6-slim

RUN pip install xgboost==0.82 scikit-learn

RUN mkdir -p /app
ADD xgboost_iris.py.py /app/

쿠버네티스 잡 실행하기

컨테이너 이미지를 빌드하고, 컨테이너 이미지 레지스트리에 푸시 한 다음, 쿠버네티스 잡(Job)을 생성하겠습니다.

Job을 생성할 때는 모델을 저장하기 위해서 PVC를 마운트 해줍니다. 이 일련의 작업들은 직접 실행 할 수 있습니다. 하지만 좀 더 편하게 하기 위해서 앞서 배운 Kubeflow Fairing을 사용하겠습니다.

다음은 로컬 개발 환경에서 Fairing을 사용하여 컨테이너 이미지를 만들고, 쿠버네티스 잡을 실행하는 예제입니다.

fairing-local-docker.py

import uuid
from kubeflow import fairing
from kubeflow.fairing.kubernetes import utils as k8s_utils

CONTAINER_REGISTRY = 'kangwoo'

namespace = 'admin'
job_name = f'xgboost-iris-job-{uuid.uuid4().hex[:4]}'

command=["python", "xgboost_iris.py", "--model_path", "/mnt/pv/xgboost/iris/model"]
output_map = {
    "Dockerfile": "Dockerfile",
    "xgboost_iris.py": "xgboost_iris.py"
}

fairing.config.set_preprocessor('python', command=command, path_prefix="/app", output_map=output_map)

fairing.config.set_builder('docker', registry=CONTAINER_REGISTRY, image_name="xgboost-iris", dockerfile_path="Dockerfile")

fairing.config.set_deployer('job', namespace=namespace, job_name=job_name,
                            pod_spec_mutators=[k8s_utils.mounting_pvc(pvc_name='seldon-models-pvc', pvc_mount_path='/mnt/pv')],
                            cleanup=True, stream_log=True)

fairing.config.run()

fairing을 실행하면 쿠버네티스 잡이 생성되고, 학습이 완료된 모델이 지정한 경로에 저장됩니다.

XGBoost를 사용하는 SeldonDeployment 로 배포 하기

SeldonDeployment 생성

SeldonDeployment 매니페스트를 작성합니다. predictor의 구현체를 XGBOOST_SERVER 로 사용합니다. modelUri 필드로 모델 저장 위치를 지정해 줍니다. pvc 의 이름이 selon-models-pvc 이고 저장 위치가 xgboost/iris/model 이므로, pvc://seldon-models-pvc/xgboost/iris/model 라고 지정해 줍니다.

기본적으로 모델 서버는 로드한 모델의 predict_proba 메소드를 호출합니다. 만약 다른 메소드를 사용하고 싶다면 파라미터로 변경할 수 있습니다. 예를 들어 predict 메소드를 호출하게 하라면, parameters 섹션에 method 란 이름으로 값을 지정해 주면 됩니다. 다음 예제는 predict 메소드를 호출하게 설정하였습니다.

xgboost.yaml

apiVersion: machinelearning.seldon.io/v1alpha2
kind: SeldonDeployment
metadata:
  name: xgboost-iris
spec:
  name: xgboost-iris
  predictors:
  - graph:
      children: []
      implementation: XGBOOST_SERVER
      modelUri: pvc://seldon-models-pvc/xgboost/iris/model
      name: classifier
    name: default
    replicas: 1

SeldonDeployment 를 생성합니다.

다음은 admin 네임스페이스 SeldonDeployment 를 생성하는 예제입니다.

kubectl -n admin apply -f xgboost.yaml

생성한 SeldonDeployment를 조회해 보겠습니다.

kubectl -n admin get seldondeployment xgboost-iris -o yaml

SeldonDeployment 가 정상적으로 생성되면 다음과 같은 응답 결과를 확인할 수 있습니다.

apiVersion: machinelearning.seldon.io/v1
kind: SeldonDeployment
metadata:
  ...
spec:
  ...
status:
  deploymentStatus:
    xgboost-iris-default-af1783b:
      availableReplicas: 1
      replicas: 1
  serviceStatus:
    xgboost-iris-default-classifier-seldonio-xgboostserver-rest-0-2:
      httpEndpoint: xgboost-iris-default-classifier-seldonio-xgboostserver-rest-0-2.admin:9000
      svcName: xgboost-iris-default-classifier-seldonio-xgboostserver-rest-0-2
    xgboost-iris-xgboost-iris-default:
      grpcEndpoint: xgboost-iris-xgboost-iris-default.admin:5001
      httpEndpoint: xgboost-iris-xgboost-iris-default.admin:8000
      svcName: xgboost-iris-xgboost-iris-default
  state: Available

SeldonDeploymentstateAvailable 이면 예측을 요청 할 수 있습니다.

예측 실행하기

예측을 요청하기 위해서는 모델 서버에 접근해야 합니다. 모델 서버는 ingressgateway 를 통해서 접근할 수 있습니다. ingressgateway 는 모델 서버들을 구분하기 위해서 호스트 이름을 사용합니다. ingressgateway에 접근하 기 위한 주소는 앞서 정의한 CLUSTER_IP 를 사용하겠습니다.

예측을 요청할 데이터를 json 파일로 작성합니다.

iris-input.json

{
  "data": {
    "ndarray": [
      [6.8,  2.8,  4.8,  1.4],
      [6.0,  3.4,  4.5,  1.6]
    ]
  }
}

다음은 admin 네임스페이스의 sklearn-iris SeldonDeployment 에 예측을 요청하는 예제입니다.

MODEL_NAME=xgboost-iris
NAMESPACE=admin

INPUT_PATH=@./iris-input.json
curl -v -H "Content-Type: application/json" http://$CLUSTER_IP/seldon/${NAMESPACE}/${MODEL_NAME}/api/v1.0/predictions -d $INPUT_PATH

정상적으로 실행되면 다음과 같은 응답 결과를 확인 할 수 있습니다.

*   Trying 192.168.21.38...
* TCP_NODELAY set
* Connected to 192.168.21.38 (192.168.21.38) port 32380 (#0)
> POST /seldon/admin/xgboost-iris/api/v1.0/predictions HTTP/1.1
> Host: 192.168.21.38:32380
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 96
> 
* upload completely sent off: 96 out of 96 bytes
< HTTP/1.1 200 OK
< x-content-type-options: nosniff
< vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers
< content-type: application/json;charset=utf-8
< content-length: 262
< date: Thu, 09 Apr 2020 14:48:15 GMT
< x-envoy-upstream-service-time: 162
< server: istio-envoy
< 
{
  "meta": {
    "puid": "td0j9lrb8e9gk9620bmqp2u8i4",
    "tags": {
    },
    "routing": {
    },
    "requestPath": {
      "classifier": "seldonio/xgboostserver_rest:0.2"
    },
    "metrics": []
  },
  "data": {
    "names": [],
    "ndarray": [1, 1]
  }
}

Seldon Core – SKLearn Server

학습이 완료된 SKLearn 모델을 피클(pickle)로 저장 한 경우에는 Seldon의 사전 패키지 된 SKLearn 서버를 사용하여 간단히 배포 할 수 있습니다.

전제 조건

  • 모델 피클은 joblib을 사용하여 저장해야 합니다. 그리고 파일명은 model.joblib 이어야 합니다.
  • 현재 sklearn 0.20.3 버전을 사용합니다. 피클 모델은 이 버전과 호환되어야 합니다.

모델 생성

SKLearn 서버를 사용하기 위해 파이썬을 사용한 간단한 scikit-learn 모델을 생성하겠습니다.

Scikit-learn의 기본적인 데이터셋 중의 하나인 아이리스 꽃 데이터를 사용하여, 아이리스 꽃을 분류하는 모델을 작성해 보겠습니다. 모델 피클은 joblib을 사용하여 저장해야 하고, 파일명은 model.joblib 이어야 합니다.

from joblib import dump
from sklearn import datasets
from sklearn import svm

clf = svm.SVC(gamma='scale')
iris = datasets.load_iris()
X, y = iris.data, iris.target
clf.fit(X, y)
dump(clf, 'model.joblib')

생성 된 모델을 사용하여 scikit-learn 서버를 실행하고 예측을 수행 할 수 있습니다. 모델은 PV, S3 호환 가능 개체 저장소, Azure Blob 저장소 또는 Google Cloud Storage에 있을 수 있습니다.

모델 저장하기

쿠버네티스의 퍼시스턴스 볼륨에 모델을 저장해 보겠습니다. PVC 는 앞서 생성한 seldon-models-pvc 을 사용하겠습니다. 모델을 학습시키기 위해서 쿠버네티스 잡(Job)을 사용하겠습니다. Job을 생성할 때 모델을 저장하기 위한 PVC를 마운트 해줍니다.

모델 코드 작성하기

아이리스 꽃을 분류하는 간단한 모델입니다. 모델을 저장할 위치를 --model_path 파라미터로 입력받게 하였습니다.

iris.py

import argparse
import os

from joblib import dump
from sklearn import datasets
from sklearn import svm

def train():
    parser = argparse.ArgumentParser()
    parser.add_argument('--model_path', default='/mnt/pv/models/sklearn/iris', type=str)
    args = parser.parse_args()

    if not (os.path.isdir(args.model_path)):
        os.makedirs(args.model_path)

    model_file = os.path.join(args.model_path, 'model.joblib')

    clf = svm.SVC(gamma='scale')
    iris = datasets.load_iris()
    X, y = iris.data, iris.target
    clf.fit(X, y)
    dump(clf, model_file)

if __name__ == '__main__':
    train()

컨테이너 이미지를 만들기

컨테이너 이미지를 만들기 위한 Dockerfile 입니다. 파이썬을 기본 이미지로 사용하고, scikit-learn 패키지를 추가로 설치합니다.

Dockerfile

FROM python:3.6-slim

RUN pip install scikit-learn==0.20.3 joblib

RUN mkdir -p /app
ADD iris.py /app/

쿠버네티스 잡 실행하기

컨테이너 이미지를 빌드하고, 컨테이너 이미지 레지스트리에 푸시 한 다음, 쿠버네티스 잡(Job)을 생성하겠습니다.

Job을 생성할 때는 모델을 저장하기 위해서 PVC를 마운트 해줍니다. 이 일련의 작업들은 직접 실행 할 수 있습니다. 하지만 좀 더 편하게 하기 위해서 앞서 배운 Kubeflow Fairing을 사용하겠습니다.

다음은 로컬 개발 환경에서 Fairing을 사용하여 컨테이너 이미지를 만들고, 쿠버네티스 잡을 실행하는 예제입니다.

fairing-local-docker.py

import uuid
from kubeflow import fairing
from kubeflow.fairing.kubernetes import utils as k8s_utils

CONTAINER_REGISTRY = 'kangwoo'

namespace = 'admin'
job_name = f'sklean-iris-job-{uuid.uuid4().hex[:4]}'

command=["python", "iris.py", "--model_path", "/mnt/pv/models/sklearn/iris"]
output_map = {
    "Dockerfile": "Dockerfile",
    "iris.py": "iris.py"
}

fairing.config.set_preprocessor('python', command=command, path_prefix="/app", output_map=output_map)

fairing.config.set_builder('docker', registry=CONTAINER_REGISTRY, image_name="sklean-iris", dockerfile_path="Dockerfile")

fairing.config.set_deployer('job', namespace=namespace, job_name=job_name,
                            pod_spec_mutators=[k8s_utils.mounting_pvc(pvc_name='kfserving-models-pvc', pvc_mount_path='/mnt/pv')],
                            cleanup=False, stream_log=True)

fairing.config.run()

fairing을 실행하면 쿠버네티스 잡이 생성되고, 학습이 완료된 모델이 지정한 경로에 저장됩니다.

SKLearn을 사용하는 SeldonDeployment 로 배포 하기

SeldonDeployment 생성

SeldonDeployment 매니페스트를 작성합니다. predictor의 구현체를 SKLEARN_SERVER 로 사용합니다. modelUri 필드로 모델 저장 위치를 지정해 줍니다. pvc 의 이름이 kfserving-models-pvc 이고 저장 위치가 models/sklearn/iris 이므로, pvc://kfserving-models-pvc/models/sklearn/iris 라고 지정해 줍니다.

기본적으로 모델 서버는 로드한 모델의 predict_proba 메소드를 호출합니다. 만약 다른 메소드를 사용하고 싶다면 파라미터로 변경할 수 있습니다. 예를 들어 predict 메소드를 호출하게 하라면, parameters 섹션에 method 란 이름으로 값을 지정해 주면 됩니다. 다음 예제는 predict 메소드를 호출하게 설정하였습니다.

sklearn.yaml

apiVersion: machinelearning.seldon.io/v1alpha2
kind: SeldonDeployment
metadata:
  name: sklearn-iris
spec:
  name: sklearn-iris
  predictors:
  - graph:
      children: []
      implementation: SKLEARN_SERVER
      modelUri: pvc://seldon-models-pvc/sklearn/iris/model
      name: classifier
      parameters:
        - name: method
          type: STRING
          value: predict
    name: default
    replicas: 1

SeldonDeployment 를 생성합니다.

다음은 admin 네임스페이스 SeldonDeployment 를 생성하는 예제입니다.

kubectl -n admin apply -f sklearn.yaml

생성한 SeldonDeployment를 조회해 보겠습니다.

kubectl -n admin get seldondeployment sklearn-iris -o yaml

SeldonDeployment 가 정상적으로 생성되면 다음과 같은 응답 결과를 확인할 수 있습니다.

apiVersion: machinelearning.seldon.io/v1
kind: SeldonDeployment
metadata:
  ...
spec:
  ...
status:
  deploymentStatus:
    sklearn-iris-default-4903e3c:
      availableReplicas: 1
      replicas: 1
  serviceStatus:
    sklearn-iris-default-classifier-seldonio-sklearnserver-rest-0-2:
      httpEndpoint: sklearn-iris-default-classifier-seldonio-sklearnserver-rest-0-2.admin:9000
      svcName: sklearn-iris-default-classifier-seldonio-sklearnserver-rest-0-2
    sklearn-iris-sklearn-iris-default:
      grpcEndpoint: sklearn-iris-sklearn-iris-default.admin:5001
      httpEndpoint: sklearn-iris-sklearn-iris-default.admin:8000
      svcName: sklearn-iris-sklearn-iris-default
  state: Available

SeldonDeploymentstateAvailable 이면 예측을 요청 할 수 있습니다.

예측 실행하기

예측을 요청하기 위해서는 모델 서버에 접근해야 합니다. 모델 서버는 ingressgateway 를 통해서 접근할 수 있습니다. ingressgateway 는 모델 서버들을 구분하기 위해서 호스트 이름을 사용합니다. ingressgateway에 접근하 기 위한 주소는 앞서 정의한 CLUSTER_IP 를 사용하겠습니다.

예측을 요청할 데이터를 json 파일로 작성합니다.

iris-input.json

{
  "data": {
    "ndarray": [
      [6.8,  2.8,  4.8,  1.4],
      [6.0,  3.4,  4.5,  1.6]
    ]
  }
}

다음은 admin 네임스페이스의 sklearn-iris SeldonDeployment 에 예측을 요청하는 예제입니다.

MODEL_NAME=sklearn-iris
NAMESPACE=admin

INPUT_PATH=@./iris-input.json
curl -v -H "Content-Type: application/json" http://$CLUSTER_IP/seldon/${NAMESPACE}/${MODEL_NAME}/api/v1.0/predictions -d $INPUT_PATH

정상적으로 실행되면 다음과 같은 응답 결과를 확인 할 수 있습니다.

*   Trying 192.168.21.38...
* TCP_NODELAY set
* Connected to 192.168.21.38 (192.168.21.38) port 32380 (#0)
> POST /seldon/admin/sklearn-iris/api/v1.0/predictions HTTP/1.1
> Host: 192.168.21.38:32380
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 96
> 
* upload completely sent off: 96 out of 96 bytes
< HTTP/1.1 200 OK
< x-content-type-options: nosniff
< vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers
< content-type: application/json;charset=utf-8
< content-length: 262
< date: Thu, 09 Apr 2020 11:28:59 GMT
< x-envoy-upstream-service-time: 13
< server: istio-envoy
< 
{
  "meta": {
    "puid": "qnac0ge2lgb3m069fbk1pf04vq",
    "tags": {
    },
    "routing": {
    },
    "requestPath": {
      "classifier": "seldonio/sklearnserver_rest:0.2"
    },
    "metrics": []
  },
  "data": {
    "names": [],
    "ndarray": [1, 1]
  }
}

Seldon Core – 사전 패키지된 추론 서버들

Kubeflow 에 함께 포함된 Selcon Core 를 이용하여 추론 서버를 배포하는 방법에 대해서 알아 보겠습니다.

Istio IngressGateway에 접근하기

Kubeflow 에 함께 포함된 Selcon Core는 트래픽을 전달하기 위해서 Istio를 사용하고 있습니다. 그래서 추론 서버를 배포할 네임스페이스에 Istio와의 연결 통로인 게이트웨이를 먼저 만들어야합니다.

네임스페이스에 kubeflow-gateway라는 게이트웨이를 만듭니다. 이 게이트웨이를 통해서 요청을 전달 받게 됩니다. 게이트웨이를 정의할 때, selector 를 이용하여 실제 트래픽을 받을 ingressgateway 를 지정해줘야 합니다. 기본적으로는 istio-system 네임스페이스에 있는 istio-ingressgateway 포드를 사용합니다. 그래서 서비스에 정의된 레이블인 istio: ingressgatewayselector 를 통해서 지정하였습니다. 별도의 ingressgateway 를 사용하려면 selector 의 조회 조건을 변경하면 됩니다. 예를 들어 kfserving-ingressgateway를 사용하려면, selectorkfserving: ingressgateway 를 지정하면 됩니다.

kubeflow-gateway.yaml

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: kubeflow-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - '*'
    port:
      name: http
      number: 80
      protocol: HTTP

admin 네임스페이스에 Gateway 를 생성합니다.

kubectl -n admin apply -f kubeflow-gateway.yaml

kfserving-ingressgateway를 조회해 보겠습니다.

다음은 istio-system 네임스페이스에 있는 kfserving-ingressgateway을 조회하는 예제입니다.

kubectl -n istio-system get service kfserving-ingressgateway 

KFServing이 설치된 쿠버네티스 클러스터에 따라 결과가 다르게 나옵니다. 응답 결과에 따른 크게 세가지 방법으로 접근 할 수 있습니다.

  • LoadBalancer 를 통해서 접근하기
  • NodePort를 통해서 접근하기
  • kubectl port-forward를 통해서 접근하기

LoadBalancer

쿠버네티스 클러스터가 LoadBalancer 를 지원하면 다음과 같은 결과를 얻을 수 있습니다. 서비스의 타입이 LoadBalancer 이고, EXTERNAL-IP 에 IP가 할당되어 있습니다. 이럴 경우에는 EXTERNAL-IP 를 통해서 ingressgateway에 접근할 수 있습니다.

NAME                       TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                                                                                                                                                                                   AGE
kfserving-ingressgateway   LoadBalancer   10.101.141.37   10.201.121.4  15020:30543/TCP,80:32380/TCP,443:32390/TCP,31400:32400/TCP,15011:30263/TCP,8060:32119/TCP,853:32180/TCP,15029:32156/TCP,15030:30674/TCP,15031:30230/TCP,15032:32563/TCP,15443:30995/TCP   2d23h

앞으로 만들 예제에서 사용하기 위해서 ingressgateway 의 접근 주소를 다음과 같이 정의하겠습니다. EXTERNAL-IP 주소를 사용합니다.

CLUSTER_IP=10.201.121.4

NodePort

쿠버네티스 클러스터가 LoadBalancer 를 지원하지 않거나, 서비스의 타입이 NodePort 인 경우 EXTERNAL-IP 의 값이 비어 있습니다. 이럴 경우에는 클러스터의 노드 IP 와 NodePort를 통해서 접근할 수 있습니다.

NAME                       TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                                                                                                                                                                                   AGE
kfserving-ingressgateway   LoadBalancer   10.101.141.37   <pending>     15020:30543/TCP,80:32380/TCP,443:32390/TCP,31400:32400/TCP,15011:30263/TCP,8060:32119/TCP,853:32180/TCP,15029:32156/TCP,15030:30674/TCP,15031:30230/TCP,15032:32563/TCP,15443:30995/TCP   2d23h

노드 IP는 노드를 조회하면 알 수 있습니다.

다음은 노드를 조회 하는 예제입니다.

kubectl get node -o wide

정상적으로 조회되면 다음과 같은 응답 결과가 나옵니다.

NAME     STATUS   ROLES    AGE   VERSION    INTERNAL-IP     EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
mortar   Ready    master   13d   v1.15.10   192.168.21.38   <none>        Ubuntu 18.04.3 LTS   4.15.0-91-generic   docker://18.9.9

노드가 한 개가 아닌 경우에는 여러개의 노드 정보가 출력됩니다. 해당 노드들 중에서 아무 노드의 INTERNAL-IP 를 사용하면 됩니다.

앞으로 만들 예제에서 사용하기 위해서 ingressgateway 의 접근 주소를 다음과 같이 정의하겠습니다. 노드의 IP 와 80 PORT(80:32380/TCP)의 노드 포트인 32380을 포트로 사용합니다.

CLUSTER_IP=192.168.21.38:32380

port-forward

외부에서 쿠버네티스 클러스터의 서비스에 접근할 수 없는 경우, kubectl 의 port-forward를 사용할 수 있습니다. 접근 하려는 외부 시스템에서 다음 명령어 실행하면 로컬 포트를 경유 해서 쿠버네티스 서비스에 접근할 수 있습니다.

kubectl -n istio-system port-forward svc/kfserving-ingressgateway 8080:80

포트 포워딩이 정상적으로 실행되면, 로컬포트로 ingressgateay 서비스로 접근할 수 있습니다. http://localhost:8080 처럼 선언한 로컬 포트의 주소로 접근하면, 쿠버네티스 ingressgateway 의 80 포트로 포워딩 됩니다.

앞으로 만들 예제에서 사용하기 위해서 ingressgateway 의 접근 주소를 다음과 같이 정의하겠습니다.

CLUSTER_IP=localhost:8080

PVC 생성하기

SeldonDeployment 에 사용할 모델은 PVC에 저장하겠습니다. 만약 클라우드 스토리지와 같은 다른 저장소를 사용하려면, “클라우드 저장소를 이용하여 InfeerneceService 배포와 예측”을 참조하시기 바랍니다.

seldon-models-pvc라는 PVC 매니페스트를 작성합니다.

seldon-models-pvc.yaml

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: seldon-models-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

다음 명령어를 실행하여, admin 네임스페이스에 seldon-models-pvc라는 PVC를 생성하겠습니다.

kubectl -n admin apply seldon-models-pvc.yaml

SKLearn Server

XGBoost Server

MLflow Server

Tensorflow Serving

Seldon Core – 설치

Seldon Core는 Kubeflow의 구성 요소로 포함되어 있습니다. 별도로 설치가 필요 없이 사용할 수 있습니다. 물론 Kubeflow 없이 독립적으로 설치해서 사용할 수도 있습니다.

Seldon Core 설치

Kubeflow와 함께 Seldon Core 설치

Selcon Core은 Kubeflow를 설치할때 기본적으로 설치됩니다. Kubeflow 매니페스트에 Selcon Core를 설치하는 부분이 포함되어 있습니다. Kubeflow와 함께 설치되는 KFServing의 경우 KFServing 컨트롤러는 kubeflow  네임스페이스에 배포됩니다.

Seldon Core를 사용하려면, 모델 서버를 생성할 네임스페이스가 다음과 같은지 확인해야합니다.

  • kubeflow-gateway라는 Istio 게이트웨이 가 있어야 합니다.
  • [serving.kubeflow.org/inferenceservice=enabled](<http://serving.kubeflow.org/inferenceservice=enabled>) 레이블이 추가 되어 있어야 합니다.

Kubeflow의 대시보드나 프로필 컨트롤러(Profile Controller)를 사용하여, 사용자 네임스페이스를 만드는 경우에는 Seldon Core에서 모델을 배포할 수 있도록 serving.kubeflow.org/inferenceservice: enabled 레이블이 자동으로 추가됩니다. 만약 네임스페이스를 직접 생성하는 경우에는 해당 네임스페이스에 serving.kubeflow.org/inferenceservice: enabled 레이블을 추가해야만, Selcon Core를 정상적으로 사용할 수 있습니다.

다음은 my-namespace 네임스페이스에 레이블을 추가하는 예제입니다.

kubectl label namespace my-namespace serving.kubeflow.org/inferenceservice=enabled

Kubeflow와 함게 설치된 Seldon Core는 istio를 사용하고 있습니다. Istio는 Seldon Core가 새롭게 배포한 대상들을 자동으로 연결할 수 있는 수신 게이트웨이를 제공합니다. kubeflow-gateway 라는 Istio 게이트웨이를 사용합니다.

다음은my-namespace라는 네임스페이스에 kubeflow-gateway라는 게이트웨이를 만드는 예제입니다.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: kubeflow-gateway
  namespace: my-namespace
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - '*'
    port:
      name: http
      number: 80
      protocol: HTTP

독립형 Seldon Core 설치

독립형 Seldon Core는 Helm 3를 이용하여 간단히 설치할 수 있습니다.

먼저 Seldon Core를 설치할 네임스페이스를 생성합니다.

kubectl create namespace seldon-system

helm과 seldon-charts를 이용하여 Seldon Core를 설치합니다.

helm install seldon-core seldon-core-operator \\
    --repo <https://storage.googleapis.com/seldon-charts> \\
    --set usageMetrics.enabled=true \\
    --namespace seldon-system \\
    --set istio.enabled=true