Knative - Serving

우야·2021년 6월 6일

knative의 serving을 사용하면서 궁금증

  1. 이걸 찾아본거는 serverless 어플리케이션을 만들면 내부적으로 knative의 service에서는 어떤 server를 사용하는걸까?

  2. Autoscaled은 어떤 순서로 되는것일까?

1. Knative Serving에서 내부 Server는 어떤걸 사용할까?

  • 내부적으로 Opensource를 사용하여 개발되었을거라 생각하고 Github을 찾아봤다.
    • 근데 knative에서 사용하는 activator, autoscaler에서도 server를 사용하기 때문에 어디에서 serverless server를 생성하는지 좀 더 찾아봐야 했다...
      • http, grpc차이만 조금 있지 knative, servless에 사용되는 server도 go의 server로 개발하여 사용하는것을 확인했다.
      • 그럼 어디에서 serverless server를 생성하는것일까?
    • 일단!!! src/net/http/server.go를 사용하는 것을 찾았다.
1. func main() {
	flag.Parse()
	log.Print("Hello world app started.")

	test.ListenAndServeGracefully(":8080", handler)
}


2. // ListenAndServeGracefully calls into ListenAndServeGracefullyWithPattern
// by passing handler to handle requests for "/"
func ListenAndServeGracefully(addr string, handler func(w http.ResponseWriter, r *http.Request)) {
	ListenAndServeGracefullyWithHandler(addr, http.HandlerFunc(handler))
}



3.  
// ListenAndServeGracefullyWithHandler creates an HTTP server, listens on the defined address
// and handles incoming requests with the given handler.
// It blocks until SIGTERM is received and the underlying server has shutdown gracefully.
func ListenAndServeGracefullyWithHandler(addr string, handler http.Handler) {
	server := pkgnet.NewServer(addr, handler)
	go server.ListenAndServe()

	<-signals.SetupSignalHandler()
	server.Shutdown(context.Background())
}

4. 
// NewServer returns a new HTTP Server with HTTP2 handler.
func NewServer(addr string, h http.Handler) *http.Server {
	h1s := &http.Server{
		Addr:    addr,
		Handler: h2c.NewHandler(h, &http2.Server{}),
	}

	return h1s
}


5. $http.Server는 위에 설명한 go의 src/net/http/server.go를 사용하여 server를 만드는 것이였다.
func NewServer(addr string, h http.Handler) *http.Server {
	h1s := &http.Server{
		Addr:    addr,
		Handler: h2c.NewHandler(h, &http2.Server{}),
	}

	return h1s
}
  • grpc server
// NewServer creates a gRPC server which has no service registered and has not
// started to accept requests yet.
func NewServer(opt ...ServerOption) *Server {
	opts := defaultServerOptions
	for _, o := range opt {
		o.apply(&opts)
	}
	s := &Server{
		lis:      make(map[net.Listener]bool),
		opts:     opts,
		conns:    make(map[transport.ServerTransport]bool),
		services: make(map[string]*serviceInfo),
		quit:     grpcsync.NewEvent(),
		done:     grpcsync.NewEvent(),
		czData:   new(channelzData),
	}
	chainUnaryServerInterceptors(s)
	chainStreamServerInterceptors(s)
	s.cv = sync.NewCond(&s.mu)
	if EnableTracing {
		_, file, line, _ := runtime.Caller(1)
		s.events = trace.NewEventLog("grpc.Server", fmt.Sprintf("%s:%d", file, line))
	}

	if s.opts.numServerWorkers > 0 {
		s.initServerWorkers()
	}

	if channelz.IsOn() {
		s.channelzID = channelz.RegisterServer(&channelzServer{s}, "")
	}
	return s
}

2. Autoscale 순서

  • 전체 흐름도는 아래와 같다.
  • 너무 복잡하다....
  • 아래는 요청 Data plane을 굵은 선으로 표시되어 있으며 나머지는 실제 아래 그림처럼 구성이 되기위해서 모듈간 Control plane이라고 볼수 있다.
  • 대충 데이터 흐름은 Ingress -> public service ->Pod로 전송되는 것이지만 내부적으로 istio Ingressgateway -> virtual service (destination) -> service -> pod 등의 절차로 볼수 있으며, pod로의 traffic결정은 configuration, revision, route등의 값으로 정해지는것이다.
  • Control plane은 더 아래서 설명하겠다.
  • pod가 0에서 autoscale이 되어 생성되면서 요청을 받는 과정
  1. 전제 상태로 보면, SKS는 proxy모드에 있다.
  2. Ingress로 요청이 들어온다. - 그림 0
  3. 요청은 Activator로 전달된다. - 그림 1
  4. 요청의 수가 Autoscaler로 리포팅된다. - 그림 2.1
  5. 요청은 Activator의 buffer에 담겨 있고, SKS의 private service를 watch하여 Endpoint가 생성되는것을 지쳐보고 있다. - 그림 2.2
  6. Autoscaler는 Activator에서 온 count를 보고 Decider와 PA(Pod Autoscale)를 사용하여 Revision Deployment를 생성하고(scale up N>0), SKS의 mode를 serve로 변경한다. - 그림 3,4,5.1,5.2
  7. Deployment는 Pod를 생성한다. - 그림 6
  8. Deployment, Pod가 생성되면 내부적으로 Endpoint가 생성되는데, SKS Private service는 Deployment를 watch하고 있어서, Endpoint가 생성된것을 알수 있다. 이를 확인하여 SKS에게 알려준다. - 그림 7, 8-1
  9. Activator는 SKS의 private service를 watch(5)하고 있었기때문에 Endpoint가 생성된것을 확인하고, probe(liveness, readiness) healthy를 테스트해본다.그리고 buffer에 있던 요청을 해당 pod로 보낸다.
  • pod에 요청이 없을때, Scaling이 0이 되는 과정
  1. 요청이 일정시간 발생하지 않으면 클러스터 리소스사용을 없애기위해서 이런처리를할 수 있다.
  2. pod안에는 Queue proxy컨테이너가 있는데 여기서 현재 요청받는게 없다는 것을 Autoscaler의 metric에 알려준다. - 그림 1
  3. Metric은 Decider를 통해 PA(Pod Autoscaler)에 이 상황을 알려준다. - 그림 2, 3
  4. PA에서는 SKS의 mode를 proxy로 변경을 알려주고, Revision의 Deployment Scale을 0으로 변경한다. - 그림 4.1, 4.2
  5. Revision의 Deployment는 Pod를 모두 삭제한다. - 그림 5
  6. 그리고 SKS의 Public Service는 Activator를 바라보며, 새로운 요청이들어오면 Activator로 보낼수 있는 상태를 만든다.

https://github.com/knative/serving/blob/d5d489c0babd5c9ae9a37d707c06df92d70711f5/docs/scaling/SYSTEM.md

profile
Fullstack developer

0개의 댓글