Use a separate controller loop to watch functions change and create a service (#544)
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
Copyright 2018 The Fission Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package poolmgr
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
apiv1 "k8s.io/client-go/pkg/api/v1"
|
||||
"k8s.io/client-go/rest"
|
||||
k8sCache "k8s.io/client-go/tools/cache"
|
||||
|
||||
"github.com/fission/fission"
|
||||
"github.com/fission/fission/crd"
|
||||
)
|
||||
|
||||
func getIstioServiceLabels(fnName string) map[string]string {
|
||||
return map[string]string{
|
||||
"functionName": fnName,
|
||||
}
|
||||
}
|
||||
|
||||
func makeFuncIstioServiceRegister(crdClient *rest.RESTClient,
|
||||
kubernetesClient *kubernetes.Clientset, fnNamespace string) k8sCache.Controller {
|
||||
|
||||
resyncPeriod := 30 * time.Second
|
||||
lw := k8sCache.NewListWatchFromClient(crdClient, "functions", metav1.NamespaceDefault, fields.Everything())
|
||||
_, controller := k8sCache.NewInformer(lw, &crd.Function{}, resyncPeriod,
|
||||
k8sCache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
fn := obj.(*crd.Function)
|
||||
|
||||
// Since istio only allows accessing pod through k8s service,
|
||||
// for the functions with executor type "poolmgr" we need to
|
||||
// create a service for sending requests to pod in pool.
|
||||
// Functions with executor type "Newdeploy" is specialized at
|
||||
// pod starts. In this case, just ignore such functions.
|
||||
fnExecutorType := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
|
||||
if fnExecutorType != fission.ExecutorTypePoolmgr {
|
||||
return
|
||||
}
|
||||
|
||||
// create a same name service for function
|
||||
// since istio only allows the traffic to service
|
||||
sel := map[string]string{
|
||||
"functionName": fn.Metadata.Name,
|
||||
"functionUid": string(fn.Metadata.UID),
|
||||
}
|
||||
|
||||
svcName := fission.GetFunctionIstioServiceName(fn.Metadata.Name, fn.Metadata.Namespace)
|
||||
|
||||
// service for accepting user traffic
|
||||
svc := apiv1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: fnNamespace,
|
||||
Name: svcName,
|
||||
Labels: getIstioServiceLabels(fn.Metadata.Name),
|
||||
},
|
||||
Spec: apiv1.ServiceSpec{
|
||||
Type: apiv1.ServiceTypeClusterIP,
|
||||
Ports: []apiv1.ServicePort{
|
||||
// Service port name should begin with a recognized prefix, or the traffic will be
|
||||
// treated as TCP traffic. (https://istio.io/docs/setup/kubernetes/sidecar-injection.html)
|
||||
// Originally the ports' name are similar to "http-fetch" and "http-specialize".
|
||||
// But for istio 0.5.1, istio-proxy return unexpected 431 error with such naming.
|
||||
// https://github.com/istio/istio/issues/928
|
||||
// Workaround: remove prefix
|
||||
// TODO: prepend prefix once the bug fixed
|
||||
{
|
||||
Name: "fetch",
|
||||
Protocol: apiv1.ProtocolTCP,
|
||||
Port: 8000,
|
||||
TargetPort: intstr.FromInt(8000),
|
||||
},
|
||||
{
|
||||
Name: "specialize",
|
||||
Protocol: apiv1.ProtocolTCP,
|
||||
Port: 8888,
|
||||
TargetPort: intstr.FromInt(8888),
|
||||
},
|
||||
},
|
||||
Selector: sel,
|
||||
},
|
||||
}
|
||||
|
||||
// create function istio service if it does not exist
|
||||
_, err := kubernetesClient.CoreV1().Services(fnNamespace).Create(&svc)
|
||||
if err != nil && !kerrors.IsAlreadyExists(err) {
|
||||
log.Printf("Error creating function istio service: %v", err)
|
||||
}
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
fn := obj.(*crd.Function)
|
||||
svcName := fission.GetFunctionIstioServiceName(fn.Metadata.Name, fn.Metadata.Namespace)
|
||||
// delete function istio service
|
||||
err := kubernetesClient.CoreV1().Services(fnNamespace).Delete(svcName, nil)
|
||||
if err != nil && !kerrors.IsNotFound(err) {
|
||||
log.Printf("Error deleting function istio service: %v", err)
|
||||
}
|
||||
},
|
||||
UpdateFunc: func(oldObj, newObj interface{}) {},
|
||||
})
|
||||
|
||||
return controller
|
||||
}
|
||||
+7
-14
@@ -28,7 +28,6 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -105,7 +104,8 @@ func MakeGenericPool(
|
||||
initialReplicas int32,
|
||||
namespace string,
|
||||
fsCache *fscache.FunctionServiceCache,
|
||||
instanceId string) (*GenericPool, error) {
|
||||
instanceId string,
|
||||
enableIstio bool) (*GenericPool, error) {
|
||||
|
||||
log.Printf("Creating pool for environment %v", env.Metadata)
|
||||
|
||||
@@ -122,16 +122,6 @@ func MakeGenericPool(
|
||||
runtimeImagePullPolicy = "IfNotPresent"
|
||||
}
|
||||
|
||||
enableIstio := false
|
||||
|
||||
if len(os.Getenv("ENABLE_ISTIO")) > 0 {
|
||||
istio, err := strconv.ParseBool(os.Getenv("ENABLE_ISTIO"))
|
||||
if err != nil {
|
||||
log.Println("Failed to parse ENABLE_ISTIO")
|
||||
}
|
||||
enableIstio = istio
|
||||
}
|
||||
|
||||
// TODO: in general we need to provide the user a way to configure pools. Initial
|
||||
// replicas, autoscaling params, various timeouts, etc.
|
||||
gp := &GenericPool{
|
||||
@@ -479,7 +469,10 @@ func (gp *GenericPool) createPool() error {
|
||||
|
||||
// Use long terminationGracePeriodSeconds for connection draining in case that
|
||||
// pod still runs user functions.
|
||||
var gracePeriodSeconds int64 = 6 * 60
|
||||
gracePeriodSeconds := int64(6 * 60)
|
||||
if gp.env.Spec.TerminationGracePeriod > 0 {
|
||||
gracePeriodSeconds = gp.env.Spec.TerminationGracePeriod
|
||||
}
|
||||
|
||||
podAnnotation := make(map[string]string)
|
||||
|
||||
@@ -750,7 +743,7 @@ func (gp *GenericPool) GetFuncSvc(m *metav1.ObjectMeta) (*fscache.FuncSvc, error
|
||||
log.Printf("Specialized pod: %v", pod.ObjectMeta.Name)
|
||||
|
||||
var svcHost string
|
||||
if gp.useSvc {
|
||||
if gp.useSvc && !gp.useIstio {
|
||||
svcName := fmt.Sprintf("svc-%v", m.Name)
|
||||
if len(m.UID) > 0 {
|
||||
svcName = fmt.Sprintf("%s-%v", svcName, m.UID)
|
||||
|
||||
+27
-2
@@ -17,11 +17,15 @@ limitations under the License.
|
||||
package poolmgr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
k8sCache "k8s.io/client-go/tools/cache"
|
||||
|
||||
"github.com/fission/fission"
|
||||
"github.com/fission/fission/crd"
|
||||
@@ -45,6 +49,9 @@ type (
|
||||
fsCache *fscache.FunctionServiceCache
|
||||
instanceId string
|
||||
requestChannel chan *request
|
||||
|
||||
enableIstio bool
|
||||
istioServiceRegister k8sCache.Controller
|
||||
}
|
||||
request struct {
|
||||
requestType
|
||||
@@ -61,7 +68,6 @@ type (
|
||||
func MakeGenericPoolManager(
|
||||
fissionClient *crd.FissionClient,
|
||||
kubernetesClient *kubernetes.Clientset,
|
||||
fissionNamespace string,
|
||||
functionNamespace string,
|
||||
fsCache *fscache.FunctionServiceCache,
|
||||
instanceId string) *GenericPoolManager {
|
||||
@@ -78,9 +84,28 @@ func MakeGenericPoolManager(
|
||||
go gpm.service()
|
||||
go gpm.eagerPoolCreator()
|
||||
|
||||
if len(os.Getenv("ENABLE_ISTIO")) > 0 {
|
||||
istio, err := strconv.ParseBool(os.Getenv("ENABLE_ISTIO"))
|
||||
if err != nil {
|
||||
log.Println("Failed to parse ENABLE_ISTIO")
|
||||
}
|
||||
gpm.enableIstio = istio
|
||||
|
||||
if gpm.enableIstio {
|
||||
gpm.istioServiceRegister = makeFuncIstioServiceRegister(
|
||||
gpm.fissionClient.GetCrdClient(), gpm.kubernetesClient, functionNamespace)
|
||||
}
|
||||
}
|
||||
|
||||
return gpm
|
||||
}
|
||||
|
||||
func (gpm *GenericPoolManager) Run(ctx context.Context) {
|
||||
if gpm.enableIstio && gpm.istioServiceRegister != nil {
|
||||
go gpm.istioServiceRegister.Run(ctx.Done())
|
||||
}
|
||||
}
|
||||
|
||||
func (gpm *GenericPoolManager) service() {
|
||||
for {
|
||||
req := <-gpm.requestChannel
|
||||
@@ -97,7 +122,7 @@ func (gpm *GenericPoolManager) service() {
|
||||
|
||||
pool, err = MakeGenericPool(
|
||||
gpm.fissionClient, gpm.kubernetesClient, req.env, poolsize,
|
||||
gpm.namespace, gpm.fsCache, gpm.instanceId)
|
||||
gpm.namespace, gpm.fsCache, gpm.instanceId, gpm.enableIstio)
|
||||
if err != nil {
|
||||
req.responseChannel <- &response{error: err}
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user