1. Installation

  • Cert-Manager: version 1.3 이하만 됩니다. 체크 필요! (나중에 에러남)
  • Istio 따로 설치 하지 말고, 여기 있는 문서에서 Knative 먼저 설치후 추가로 Networking Layer 설정시에 Istio 설정함

1.1 Check Version

# Knavtive Server 버젼 확인
$ kubectl get namespace knative-serving -o 'go-template={{index .metadata.labels "serving.knative.dev/release"}}'
v0.11.2

# Knative Eventing 버젼 확인 
$ kubectl get namespace knative-eventing -o 'go-template={{index .metadata.labels "eventing.knative.dev/release"}}'

1.2 Install Knative CLI

kn 명령을 내릴수 있는 클라이언트를 먼저 설치 합니다.

$ wget https://storage.googleapis.com/knative-nightly/client/latest/kn-linux-amd64
$ chmod 110 kn-linux-amd64
$ sudo mv kn-linux-amd64 /usr/local/bin/kn

1.3 Delete Existing Serving Component

강제로 업그레이드시 apply를 delete로 변경하고 이후 apply진행합니다. (서비스 안하고 있다는 전제)
일단 삭제이유는 kubeflow로 설치이후 버젼 업그레이드가 목적입니다.
귀찮아서 업그레이드 방식 따르지 않고 그냥 다 삭제해버리고 다시 깔아버리는 방법입니다.
바로 아래 코드는 서비스를 안하고 있을경우 강제로 업그레이드 하기 위해서 먼저 삭제부터 하는 코드 입니다.
주의하세요!!

$ kubectl get namespaces knative-serving -o json | jq '.spec.finalizers'=null | kubectl apply -f -
$ kubectl delete -f https://storage.googleapis.com/knative-nightly/serving/latest/serving-core.yaml
$ kubectl delete -f https://storage.googleapis.com/knative-nightly/serving/latest/serving-crds.yaml
$ kubectl delete -f https://github.com/knative/operator/releases/download/knative-v1.1.0/operator.yaml
$ kubectl get namespaces  knative-serving 
NAME              STATUS        AGE
knative-serving   Terminating   44m

위를 확인해보면 Terminating에서 멈춰서 더이상 진행이 되지 않습니다.
Finelizer 부분을 설정해줘야 합니다.
참고로 그냥 edit으로 안됩니다.

$ kubectl get namespaces knative-serving -o json > knative-serving.json
$ vi knative-serving.json

안에 보면 finalizers 라고 있을겁니다.

    "spec": {
        "finalizers": [
            "kubernetes"
        ]
    }

finalizers 부분을 삭제하고 저장합니다.

    "spec": {
        "finalizers": [
        ]
    }

주소를 알아내고, 편집한 json 파일로 대체를 합니다.
이때 알아낸 selfLink의 주소 뒤에 /finalize 를 붙여줘야 합니다.

$ kubectl get namespace knative-serving -o json | jq .metadata.selfLink
"/api/v1/namespaces/knative-serving"

$ kubectl replace --raw "/api/v1/namespaces/knative-serving/finalize" -f ./knative-serving.json

이후 kubectl get namespaces 로 확인해보면 삭제되어 있을겁니다.

1.4 Install Serving Component

Serving을 설치합니다.

$ kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.1.0/serving-crds.yaml
$ kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.1.0/serving-core.yaml

Knative Component를 설치합니다.
자세한 설치방법은 링크 를 참조 합니다.

  • Knative Operator 는 default namespace에 설치가 되야 합니다.
$ kubectl config set-context --current --namespace=default
$ kubectl apply -f https://github.com/knative/operator/releases/download/knative-v1.1.0/operator.yaml
$ kubectl get deployment knative-operator
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
knative-operator   1/1     1            1           20s

1.5 Install Istio

반드시 아래에 있는 방법으로 설치를 해야지 Knative가 정상 작동합니다.
따로 Istio에 있는 문서대로 하면 설치가 안됩니다.

$ kubectl apply -l knative.dev/crd-install=true -f https://github.com/knative/net-istio/releases/download/knative-v1.1.0/istio.yaml
$ kubectl apply -f https://github.com/knative/net-istio/releases/download/knative-v1.1.0/istio.yaml
$ kubectl apply -f https://github.com/knative/net-istio/releases/download/knative-v1.1.0/net-istio.yaml

# 확인
$ kubectl --namespace istio-system get service istio-ingressgateway

1.5 cluster-local-gateway 설정

자세한 설치방법은 링크 를 참조 합니다.

cat << EOF > ./istio-minimal-operator.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    global:
      proxy:
        autoInject: disabled
      useMCP: false
      # The third-party-jwt is not enabled on all k8s.
      # See: https://istio.io/docs/ops/best-practices/security/#configure-third-party-service-account-tokens
      jwtPolicy: first-party-jwt

  addonComponents:
    pilot:
      enabled: true
    prometheus:
      enabled: false

  components:
    ingressGateways:
      - name: istio-ingressgateway
        enabled: true
      - name: cluster-local-gateway
        enabled: true
        label:
          istio: cluster-local-gateway
          app: cluster-local-gateway
        k8s:
          service:
            type: ClusterIP
            ports:
            - port: 15020
              name: status-port
            - port: 80
              targetPort: 8080
              name: http2
            - port: 443
              targetPort: 8443
              name: https
EOF

실행

$ istioctl install -f istio-minimal-operator.yaml
This will install the Istio 1.11.0 default profile with ["Istio core" "Istiod" "Ingress gateways"] components into the cluster. Proceed? (y/N) y
✔ Istio core installed                                                                                                                                                                                                                        
✔ Istiod installed                                                                                                                                                                                                                            
✔ Ingress gateways installed                                                                                                                                                                                                                  
✔ Installation complete                                                                                                                                                                                                                       
Thank you for installing Istio 1.11.

# 설치 확인
$ kubectl get pods --namespace istio-system
NAME                                    READY   STATUS    RESTARTS   AGE
cluster-local-gateway-c9d6b9dbf-2tqh5   1/1     Running   0          5m55s
istio-ingressgateway-754f55757d-sqgrk   1/1     Running   0          5m55s
istiod-59b95457f9-x5fcc                 1/1     Running   0          8m19s

$ kubectl get svc -n istio-system
NAME                    TYPE           CLUSTER-IP       EXTERNAL-IP                                                                   PORT(S)                                      AGE
cluster-local-gateway   ClusterIP      172.20.87.103    <none>                                                                        15020/TCP,80/TCP,443/TCP                     6m31s
istio-ingressgateway    LoadBalancer   172.20.107.71    a1234567890abcdefghijklmnopqrstu-123456789.ap-northeast-2.elb.amazonaws.com   15021:31476/TCP,80:30369/TCP,443:31261/TCP   62m
istiod                  ClusterIP      172.20.138.114   <none>                                                                        15010/TCP,15012/TCP,443/TCP,15014/TCP        62m
knative-local-gateway   ClusterIP      172.20.106.201   <none>                                                                        80/TCP                                       15m

1.6 Install Eventing Component

  • KServing 설치시 필요없는 component

Custom Resource Definitions (CRD) 그리고 핵심 core components를 설치 합니다.

$ kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.1.0/eventing-crds.yaml
$ kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.1.0/eventing-core.yaml

모두 정상 작동 하는지 확인

$ kubectl get pods -n knative-eventing
NAME                                   READY   STATUS    RESTARTS   AGE
eventing-controller-56ccd89cd8-xxn9c   1/1     Running   0          39s
eventing-webhook-76b66cd56c-vrpzc      1/1     Running   0          38s

Broker Layer는 Apache Kafka, MT-Channel-based, RabbitMQ 등을 지원하고 있습니다.
MT Broker는 다음과 같이 설치 합니다.
자세한 내용은 링크를 확인합니다.

$ kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.1.0/mt-channel-broker.yaml

2. Configuration

2.1 Custom Domain Setup

Knative Serving Route는 기본값으로 example.com 을 기본 도메인으로 사용합니다.
이후 KFServing을 할때도 모두 example.com 으로 나오기 때문에 이 부분의 변경이 반드시 필요 합니다.

$ kubectl edit cm config-domain --namespace knative-serving

처음 열면 아래와 같은 형식으로 생겼는데..
example:_| 로 시작하는 부분을 모두 삭제 합니다.
example.com 까지 모두 삭제 했으면, example.com 도메인을 변경합니다.

apiVersion: v1
data:
  _example: |
    ################################
    #                              #
    #    EXAMPLE CONFIGURATION     #
    #                              #
    ################################
    # ...
    example.com: |
kind: ConfigMap

변경이후 다음과 같습니다.

apiVersion: v1
data:
  example.org: |
    selector:
      app: nonprofit
  ml.incredible.ai: ""
  svc.cluster.local: |
    selector:
      app: cluster-local

3. Getting Started

3.1 Hello World in Python

Python App

cat <<EOF > app.py
import os
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    target = os.environ.get('TARGET', 'World')
    return 'Hello {}!\n'.format(target)
if __name__ == "__main__":
    app.run(debug=True,host='0.0.0.0',port=8080)
EOF

Dockerfile & Build

cat <<EOF > Dockerfile
FROM python:3.7-slim
ENV PYTHONUNBUFFERED=True
ENV APP_HOME=/app
WORKDIR \$APP=HOME
COPY . ./
RUN pip install Flask gunicorn
CMD exec gunicorn --bind :\$PORT --workers 1 --threads 8 --timeout 0 app:app
EOF
$ docker login
$ docker build -t andersonjo/knative-hello-world .
$ docker push andersonjo/knative-hello-world

Service & Deploy
andersonjo 부분을 Docker Hub 아이디로 변경 필요

cat <<EOF > service.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: helloworld-python
  namespace: knative-samples
spec:
  template:
    spec:
      containers:
        - name: helloworld-python
          image: docker.io/andersonjo/knative-hello-world
          imagePullPolicy: IfNotPresent 
          env:
          - name: TARGET
            value: "Python Sample v1"
EOF
$ kubectl apply -f service.yaml
$ kubectl get ksvc helloworld-python -n knative-samples
NAME                URL                                            LATESTCREATED             LATESTREADY               READY   REASON
helloworld-python   http://helloworld-python.default.ai.platform   helloworld-python-69cbt   helloworld-python-69cbt   True    
$ INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
$ curl -H "Host:helloworld-python.default.ai.platform" $INGRESS_HOST
$ curl -H "Host:helloworld-python.default.ml.inthewear.com" $INGRESS_HOST
Hello Python Sample v1!

3. Autoscale

3.1 Autoscaler Types

  1. Knative Pod Autoscaler (KPA)
    • Knative Serving Core의 한 부분이며, 디폴트값이 사용이다.
    • 트래픽이 없을경우 0으로 scale 하는 기능도 있음 (scale down)
    • CPU 기반의 autoscaling은 지원하지 않음
  2. Horizontal Pod Autoscaler (HPA)
    • Knative Serving Core에서 제공되는 기능이 아니며, Knative Serving 설치이후 따로 설정해줘야 함
    • 트래픽이 없을 경우 0으로 scale down하는 기능이 없음
    • CPU 기반의 autoscaling 제공

설정은 class annotation을 설정해서 타입을 설정 할 수 있습니다.

  • Global Settings Key: pod-autoscaler-class
  • Per-revision annotation key: autoscaling.knative.dev/class
  • Possible values: kpa.autoscaling.knative.dev or hpa.autoscaling.knative.dev
  • Default: kpa.autoscaling.knative.dev

3.2 Configuration

3.2.1 Per Revision Example

spec.template.metadata.annotations 여기에서 autoscaling.knative.dev/class 설정

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: helloworld-go
  namespace: default
spec:
  template:
    metadata:
      annotations:
        autoscaling.knative.dev/class: "kpa.autoscaling.knative.dev"
    spec:
      containers:
        - image: gcr.io/knative-samples/helloworld-go

3.2.2 Global(ConfigMap)

$ kubectl edit configmap config-autoscaler -n knative-serving 
apiVersion: v1
kind: ConfigMap
metadata:
 name: config-autoscaler
 namespace: knative-serving
data:
 pod-autoscaler-class: "kpa.autoscaling.knative.dev"

3.2.3 Global(Operator)

$ kubectl edit configmap config-autoscaler -n knative-serving 
apiVersion: operator.knative.dev/v1alpha1
kind: KnativeServing
metadata:
  name: knative-serving
spec:
  config:
    autoscaler:
      pod-autoscaler-class: "kpa.autoscaling.knative.dev"

3.3 Example

cat <<EOF > service.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: autoscale-go
  namespace: default
spec:
  template:
    metadata:
      annotations:
        # Target 10 in-flight-requests per pod.
        autoscaling.knative.dev/target: "10"
    spec:
      containers:
      - image: gcr.io/knative-samples/autoscale-go:0.1
EOF
$ kubectl apply -f service.yaml
$ kubectl get ksvc autoscale-go
NAME           URL                                       LATESTCREATED        LATESTREADY          READY   REASON
autoscale-go   http://autoscale-go.default.ai.platform   autoscale-go-xdstf   autoscale-go-xdstf   True    
$ INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
$ curl  -H "Host:autoscale-go.default.ai.platform" "${INGRESS_HOST}?sleep=500&prime=10000&bloat=5"
Allocated 5 Mb of memory.
The largest prime less than 10000 is 9973.
Slept for 500.11 milliseconds.

Load Test를 진행합니다.

$ hey -z 30s -c 200 -H "Host: autoscale-go.default.ai.platform" "http://${INGRESS_HOST}?sleep=5000&prime=1000000&bloat=20"
$ kubectl get pods
NAME                                                              READY   STATUS    RESTARTS   AGE
autoscale-go-2fkh2-deployment-65bc9d7764-zq2sr                    2/2     Running   0          5m17s
autoscale-go-5n6gm-deployment-657678f8bd-xp87n                    2/2     Running   0          6m31s
autoscale-go-dm54z-deployment-c48fcb7ff-49ckr                     2/2     Running   0          179m